Răsfoiți Sursa

Merge pull request #494 from shamanDevel/OpenCL

OpenCL for jME3
empirephoenix 9 ani în urmă
părinte
comite
fbf2dd4497
76 a modificat fișierele cu 13376 adăugiri și 6 ștergeri
  1. 6 0
      jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java
  2. 62 0
      jme3-core/src/main/java/com/jme3/opencl/AbstractOpenCLObject.java
  3. 427 0
      jme3-core/src/main/java/com/jme3/opencl/Buffer.java
  4. 68 0
      jme3-core/src/main/java/com/jme3/opencl/CommandQueue.java
  5. 439 0
      jme3-core/src/main/java/com/jme3/opencl/Context.java
  6. 91 0
      jme3-core/src/main/java/com/jme3/opencl/DefaultPlatformChooser.java
  7. 311 0
      jme3-core/src/main/java/com/jme3/opencl/Device.java
  8. 59 0
      jme3-core/src/main/java/com/jme3/opencl/Event.java
  9. 537 0
      jme3-core/src/main/java/com/jme3/opencl/Image.java
  10. 628 0
      jme3-core/src/main/java/com/jme3/opencl/Kernel.java
  11. 58 0
      jme3-core/src/main/java/com/jme3/opencl/KernelCompilationException.java
  12. 58 0
      jme3-core/src/main/java/com/jme3/opencl/MappingAccess.java
  13. 52 0
      jme3-core/src/main/java/com/jme3/opencl/MemoryAccess.java
  14. 78 0
      jme3-core/src/main/java/com/jme3/opencl/OpenCLException.java
  15. 75 0
      jme3-core/src/main/java/com/jme3/opencl/OpenCLObject.java
  16. 123 0
      jme3-core/src/main/java/com/jme3/opencl/OpenCLObjectManager.java
  17. 107 0
      jme3-core/src/main/java/com/jme3/opencl/Platform.java
  18. 54 0
      jme3-core/src/main/java/com/jme3/opencl/PlatformChooser.java
  19. 104 0
      jme3-core/src/main/java/com/jme3/opencl/Program.java
  20. 229 0
      jme3-core/src/main/java/com/jme3/opencl/ProgramCache.java
  21. 163 0
      jme3-core/src/main/java/com/jme3/opencl/package-info.java
  22. 3 0
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java
  23. 34 1
      jme3-core/src/main/java/com/jme3/system/AppSettings.java
  24. 5 0
      jme3-core/src/main/java/com/jme3/system/JmeContext.java
  25. 6 0
      jme3-core/src/main/java/com/jme3/system/NullContext.java
  26. 224 0
      jme3-core/src/main/resources/Common/OpenCL/Matrix3f.clh
  27. 319 0
      jme3-core/src/main/resources/Common/OpenCL/Matrix4f.clh
  28. 185 0
      jme3-core/src/main/resources/Common/OpenCL/Random.clh
  29. 6 0
      jme3-desktop/src/main/java/com/jme3/system/awt/AwtPanelsContext.java
  30. 1 0
      jme3-examples/build.gradle
  31. 311 0
      jme3-examples/src/main/java/jme3test/opencl/HelloOpenCL.java
  32. 255 0
      jme3-examples/src/main/java/jme3test/opencl/TestContextSwitching.java
  33. 174 0
      jme3-examples/src/main/java/jme3test/opencl/TestMultipleApplications.java
  34. 403 0
      jme3-examples/src/main/java/jme3test/opencl/TestOpenCLLibraries.java
  35. 186 0
      jme3-examples/src/main/java/jme3test/opencl/TestVertexBufferSharing.java
  36. 183 0
      jme3-examples/src/main/java/jme3test/opencl/TestWriteToTexture.java
  37. 4 0
      jme3-examples/src/main/resources/jme3test/opencl/Blas.cl
  38. 27 0
      jme3-examples/src/main/resources/jme3test/opencl/ContextSwitchingScreen.xml
  39. 99 0
      jme3-examples/src/main/resources/jme3test/opencl/JuliaSet.cl
  40. 7 0
      jme3-ios/src/main/java/com/jme3/system/ios/IGLESContext.java
  41. 1 0
      jme3-jogl/build.gradle
  42. 236 0
      jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclBuffer.java
  43. 84 0
      jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclCommandQueue.java
  44. 262 0
      jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclContext.java
  45. 302 0
      jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclDevice.java
  46. 100 0
      jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclEvent.java
  47. 544 0
      jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclImage.java
  48. 290 0
      jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclKernel.java
  49. 127 0
      jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclPlatform.java
  50. 194 0
      jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclProgram.java
  51. 162 0
      jme3-jogl/src/main/java/com/jme3/opencl/jocl/Utils.java
  52. 115 0
      jme3-jogl/src/main/java/com/jme3/system/jogl/JoglContext.java
  53. 236 0
      jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglBuffer.java
  54. 82 0
      jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglCommandQueue.java
  55. 240 0
      jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglContext.java
  56. 293 0
      jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglDevice.java
  57. 100 0
      jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglEvent.java
  58. 575 0
      jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglImage.java
  59. 277 0
      jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglKernel.java
  60. 127 0
      jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglPlatform.java
  61. 145 0
      jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglProgram.java
  62. 167 0
      jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/Utils.java
  63. 115 0
      jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java
  64. 4 0
      jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglDisplay.java
  65. 238 0
      jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglBuffer.java
  66. 80 0
      jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglCommandQueue.java
  67. 255 0
      jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglContext.java
  68. 303 0
      jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglDevice.java
  69. 94 0
      jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglEvent.java
  70. 579 0
      jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglImage.java
  71. 233 0
      jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglKernel.java
  72. 128 0
      jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglPlatform.java
  73. 189 0
      jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglProgram.java
  74. 163 0
      jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/Utils.java
  75. 168 5
      jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java
  76. 7 0
      jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java

+ 6 - 0
jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java

@@ -457,4 +457,10 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
             }
         });
     }
+
+    @Override
+    public com.jme3.opencl.Context getOpenCLContext() {
+        logger.warning("OpenCL is not yet supported on android");
+        return null;
+    }
 }

+ 62 - 0
jme3-core/src/main/java/com/jme3/opencl/AbstractOpenCLObject.java

@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl;
+
+/**
+ * Abstract implementation of {@link OpenCLObject} providing the release 
+ * mechanisms.
+ * @author Sebastian Weiss
+ */
+public abstract class AbstractOpenCLObject implements OpenCLObject {
+    
+    protected final ObjectReleaser releaser;
+    protected AbstractOpenCLObject(ObjectReleaser releaser) {
+        this.releaser = releaser;
+    }
+    @Override
+    public void register() {
+        OpenCLObjectManager.getInstance().registerObject(this);
+    }
+    @Override
+    public void release() {
+        releaser.release();
+    }
+    @Override
+    @SuppressWarnings("FinalizeDeclaration")
+    protected void finalize() throws Throwable {
+        release();
+    }
+    @Override
+    public ObjectReleaser getReleaser() {
+        return releaser;
+    }
+}

+ 427 - 0
jme3-core/src/main/java/com/jme3/opencl/Buffer.java

@@ -0,0 +1,427 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Wrapper for an OpenCL buffer object.
+ * A buffer object stores a one-dimensional collection of elements. Elements of a buffer object can
+ * be a scalar data type (such as an int, float), vector data type, or a user-defined structure.
+ * <br>
+ * Buffers are created by the {@link Context}.
+ * <br>
+ * All access methods (read/write/copy/map) are available in both sychronized/blocking versions
+ * and in async/non-blocking versions. The later ones always return an {@link Event} object
+ * and have the prefix -Async in their name.
+ * 
+ * @see Context#createBuffer(long, com.jme3.opencl.MemoryAccess) 
+ * @author shaman
+ */
+public abstract class Buffer extends AbstractOpenCLObject {
+
+    protected Buffer(ObjectReleaser releaser) {
+        super(releaser);
+    }
+    
+    /**
+     * @return the size of the buffer in bytes.
+     * @see Context#createBuffer(long) 
+     */
+    public abstract long getSize();
+
+    /**
+     * @return the memory access flags set on creation.
+     * @see Context#createBuffer(long, com.jme3.opencl.MemoryAccess) 
+     */
+    public abstract MemoryAccess getMemoryAccessFlags();
+
+    /**
+     * Performs a blocking read of the buffer.
+     * The target buffer must have at least {@code size} bytes remaining.
+     * This method may set the limit to the last byte read.
+     * @param queue the command queue
+     * @param dest the target buffer
+     * @param size the size in bytes being read
+     * @param offset the offset in bytes in the buffer to read from
+     */
+    public abstract void read(CommandQueue queue, ByteBuffer dest, long size, long offset);
+
+    /**
+     * Alternative version of {@link #read(com.jme3.opencl.CommandQueue, java.nio.ByteBuffer, long, long) },
+     * sets {@code offset} to zero.
+     */
+    public void read(CommandQueue queue, ByteBuffer dest, long size) {
+        read(queue, dest, size, 0);
+    }
+
+    /**
+     * Alternative version of {@link #read(com.jme3.opencl.CommandQueue, java.nio.ByteBuffer, long) },
+     * sets {@code size} to {@link #getSize() }.
+     */
+    public void read(CommandQueue queue, ByteBuffer dest) {
+        read(queue, dest, getSize());
+    }
+
+    /**
+     * Performs an async/non-blocking read of the buffer.
+     * The target buffer must have at least {@code size} bytes remaining.
+     * This method may set the limit to the last byte read.
+     * @param queue the command queue
+     * @param dest the target buffer
+     * @param size the size in bytes being read
+     * @param offset the offset in bytes in the buffer to read from
+     * @return the event indicating when the memory has been fully read into the provided buffer
+     */
+    public abstract Event readAsync(CommandQueue queue, ByteBuffer dest, long size, long offset);
+
+    /**
+     * Alternative version of {@link #readAsync(com.jme3.opencl.CommandQueue, java.nio.ByteBuffer, long, long) },
+     * sets {@code offset} to zero.
+     */
+    public Event readAsync(CommandQueue queue, ByteBuffer dest, long size) {
+        return readAsync(queue, dest, size, 0);
+    }
+
+    /**
+     * Alternative version of {@link #readAsync(com.jme3.opencl.CommandQueue, java.nio.ByteBuffer, long) },
+     * sets {@code size} to {@link #getSize() }
+     */
+    public Event readAsync(CommandQueue queue, ByteBuffer dest) {
+        return readAsync(queue, dest, getSize());
+    }
+
+    /**
+     * Performs a blocking write to the buffer.
+     * The target buffer must have at least {@code size} bytes remaining.
+     * This method may set the limit to the last byte that will be written.
+     * @param queue the command queue
+     * @param src the source buffer, its data is written to this buffer
+     * @param size the size in bytes to write
+     * @param offset the offset into the target buffer
+     */
+    public abstract void write(CommandQueue queue, ByteBuffer src, long size, long offset);
+
+    /**
+     * Alternative version of {@link #write(com.jme3.opencl.CommandQueue, java.nio.ByteBuffer, long, long) },
+     * sets {@code offset} to zero.
+     */
+    public void write(CommandQueue queue, ByteBuffer src, long size) {
+        write(queue, src, size, 0);
+    }
+
+    /**
+     * Alternative version of {@link #write(com.jme3.opencl.CommandQueue, java.nio.ByteBuffer, long) },
+     * sets {@code size} to {@link #getSize() }.
+     */
+    public void write(CommandQueue queue, ByteBuffer src) {
+        write(queue, src, getSize());
+    }
+
+    /**
+     * Performs an async/non-blocking write to the buffer.
+     * The target buffer must have at least {@code size} bytes remaining.
+     * This method may set the limit to the last byte that will be written.
+     * @param queue the command queue
+     * @param src the source buffer, its data is written to this buffer
+     * @param size the size in bytes to write
+     * @param offset the offset into the target buffer
+     * @return the event object indicating when the write operation is completed
+     */
+    public abstract Event writeAsync(CommandQueue queue, ByteBuffer src, long size, long offset);
+
+    /**
+     * Alternative version of {@link #writeAsync(com.jme3.opencl.CommandQueue, java.nio.ByteBuffer, long, long) },
+     * sets {@code offset} to zero.
+     */
+    public Event writeAsync(CommandQueue queue, ByteBuffer src, long size) {
+        return writeAsync(queue, src, size, 0);
+    }
+
+    /**
+     * Alternative version of {@link #writeAsync(com.jme3.opencl.CommandQueue, java.nio.ByteBuffer, long) },
+     * sets {@code size} to {@link #getSize() }.
+     */
+    public Event writeAsync(CommandQueue queue, ByteBuffer src) {
+        return writeAsync(queue, src, getSize());
+    }
+
+    /**
+     * Performs a blocking copy operation from this buffer to the specified buffer.
+     * @param queue the command queue
+     * @param dest the target buffer
+     * @param size the size in bytes to copy
+     * @param srcOffset offset in bytes into this buffer
+     * @param destOffset offset in bytes into the target buffer
+     */
+    public abstract void copyTo(CommandQueue queue, Buffer dest, long size, long srcOffset, long destOffset);
+
+    /**
+     * Alternative version of {@link #copyTo(com.jme3.opencl.CommandQueue, com.jme3.opencl.Buffer, long, long, long) },
+     * sets {@code srcOffset} and {@code destOffset} to zero.
+     */
+    public void copyTo(CommandQueue queue, Buffer dest, long size) {
+        copyTo(queue, dest, size, 0, 0);
+    }
+
+    /**
+     * Alternative version of {@link #copyTo(com.jme3.opencl.CommandQueue, com.jme3.opencl.Buffer, long) },
+     * sets {@code size} to {@code this.getSize()}.
+     */
+    public void copyTo(CommandQueue queue, Buffer dest) {
+        copyTo(queue, dest, getSize());
+    }
+
+    /**
+     * Performs an async/non-blocking copy operation from this buffer to the specified buffer.
+     * @param queue the command queue
+     * @param dest the target buffer
+     * @param size the size in bytes to copy
+     * @param srcOffset offset in bytes into this buffer
+     * @param destOffset offset in bytes into the target buffer
+     * @return the event object indicating when the copy operation is finished
+     */
+    public abstract Event copyToAsync(CommandQueue queue, Buffer dest, long size, long srcOffset, long destOffset);
+
+    /**
+     * Alternative version of {@link #copyToAsync(com.jme3.opencl.CommandQueue, com.jme3.opencl.Buffer, long, long, long) },
+     * sets {@code srcOffset} and {@code destOffset} to zero.
+     */
+    public Event copyToAsync(CommandQueue queue, Buffer dest, long size) {
+        return copyToAsync(queue, dest, size, 0, 0);
+    }
+
+    /**
+     * Alternative version of {@link #copyToAsync(com.jme3.opencl.CommandQueue, com.jme3.opencl.Buffer, long) },
+     * sets {@code size} to {@code this.getSize()}.
+     */
+    public Event copyToAsync(CommandQueue queue, Buffer dest) {
+        return copyToAsync(queue, dest, getSize());
+    }
+
+    /**
+     * Maps this buffer directly into host memory. This might be the fastest method
+     * to access the contents of the buffer since the OpenCL implementation directly
+     * provides the memory.<br>
+     * <b>Important:</b> The mapped memory MUST be released by calling 
+     * {@link #unmap(com.jme3.opencl.CommandQueue, java.nio.ByteBuffer) }.
+     * @param queue the command queue
+     * @param size the size in bytes to map
+     * @param offset the offset into this buffer
+     * @param access specifies the possible access to the memory: READ_ONLY, WRITE_ONLY, READ_WRITE
+     * @return the byte buffer directly reflecting the buffer contents
+     */
+    public abstract ByteBuffer map(CommandQueue queue, long size, long offset, MappingAccess access);
+
+    /**
+     * Alternative version of {@link #map(com.jme3.opencl.CommandQueue, long, long, com.jme3.opencl.MappingAccess) },
+     * sets {@code offset} to zero.
+     * <b>Important:</b> The mapped memory MUST be released by calling 
+     * {@link #unmap(com.jme3.opencl.CommandQueue, java.nio.ByteBuffer) }.
+     */
+    public ByteBuffer map(CommandQueue queue, long size, MappingAccess access) {
+        return map(queue, size, 0, access);
+    }
+
+    /**
+     * Alternative version of {@link #map(com.jme3.opencl.CommandQueue, long, com.jme3.opencl.MappingAccess) },
+     * sets {@code size} to {@link #getSize() }.
+     * <b>Important:</b> The mapped memory MUST be released by calling 
+     * {@link #unmap(com.jme3.opencl.CommandQueue, java.nio.ByteBuffer) }.
+     */
+    public ByteBuffer map(CommandQueue queue, MappingAccess access) {
+        return map(queue, getSize(), access);
+    }
+
+    /**
+     * Unmaps a previously mapped memory.
+     * This releases the native resources and for WRITE_ONLY or READ_WRITE access,
+     * the memory content is sent back to the GPU.
+     * @param queue the command queue
+     * @param ptr the buffer that was previously mapped
+     */
+    public abstract void unmap(CommandQueue queue, ByteBuffer ptr);
+
+    /**
+     * Maps this buffer asynchronously into host memory. This might be the fastest method
+     * to access the contents of the buffer since the OpenCL implementation directly
+     * provides the memory.<br>
+     * <b>Important:</b> The mapped memory MUST be released by calling 
+     * {@link #unmap(com.jme3.opencl.CommandQueue, java.nio.ByteBuffer) }.
+     * @param queue the command queue
+     * @param size the size in bytes to map
+     * @param offset the offset into this buffer
+     * @param access specifies the possible access to the memory: READ_ONLY, WRITE_ONLY, READ_WRITE
+     * @return the byte buffer directly reflecting the buffer contents
+     * and the event indicating when the buffer contents are available
+     */
+    public abstract AsyncMapping mapAsync(CommandQueue queue, long size, long offset, MappingAccess access);
+    /**
+     * Alternative version of {@link #mapAsync(com.jme3.opencl.CommandQueue, long, long, com.jme3.opencl.MappingAccess) },
+     * sets {@code offset} to zero.
+     * <b>Important:</b> The mapped memory MUST be released by calling 
+     * {@link #unmap(com.jme3.opencl.CommandQueue, java.nio.ByteBuffer) }.
+     */
+    public AsyncMapping mapAsync(CommandQueue queue, long size, MappingAccess access) {
+        return mapAsync(queue, size, 0, access);
+    }
+    /**
+     * Alternative version of {@link #mapAsync(com.jme3.opencl.CommandQueue, long, com.jme3.opencl.MappingAccess) },
+     * sets {@code size} to {@link #getSize() }.
+     * <b>Important:</b> The mapped memory MUST be released by calling 
+     * {@link #unmap(com.jme3.opencl.CommandQueue, java.nio.ByteBuffer) }.
+     */
+    public AsyncMapping mapAsync(CommandQueue queue, MappingAccess access) {
+        return mapAsync(queue, getSize(), 0, access);
+    }
+    
+    /**
+     * Enqueues a fill operation. This method can be used to initialize or clear
+     * a buffer with a certain value.
+     * @param queue the command queue
+     * @param pattern the buffer containing the filling pattern.
+     *  The remaining bytes specify the pattern length
+     * @param size the size in bytes to fill, must be a multiple of the pattern length
+     * @param offset the offset in bytes into the buffer, must be a multiple of the pattern length
+     * @return an event indicating when this operation is finished
+     */
+    public abstract Event fillAsync(CommandQueue queue, ByteBuffer pattern, long size, long offset);
+
+    /**
+     * Result of an async mapping operation, contains the event and the target byte buffer.
+     * This is a work-around since no generic pair-structure is avaiable.
+     *
+     * @author shaman
+     */
+    public static class AsyncMapping {
+
+        public final Event event;
+        public final ByteBuffer buffer;
+
+        public AsyncMapping(Event event, ByteBuffer buffer) {
+            super();
+            this.event = event;
+            this.buffer = buffer;
+        }
+
+        /**
+         * @return the event object indicating when the data in the mapped buffer
+         * is available
+         */
+        public Event getEvent() {
+            return event;
+        }
+
+        /**
+         * @return the mapped buffer, only valid when the event object signals completion
+         */
+        public ByteBuffer getBuffer() {
+            return buffer;
+        }
+    }
+    
+    /**
+     * Copies this buffer to the specified image.
+     * Note that no format conversion is done.
+     * <br>
+     * For detailed description of the origin and region paramenter, see the
+     * documentation of the {@link Image} class.
+     * 
+     * @param queue the command queue
+     * @param dest the target image
+     * @param srcOffset the offset in bytes into this buffer
+     * @param destOrigin the origin of the copied area
+     * @param destRegion the size of the copied area
+     * @return the event object
+     */
+    public abstract Event copyToImageAsync(CommandQueue queue, Image dest, long srcOffset, long[] destOrigin, long[] destRegion);
+    
+    /**
+     * Aquires this buffer object for using. Only call this method if this buffer
+     * represents a shared object from OpenGL, created with e.g.
+     * {@link Context#bindVertexBuffer(com.jme3.scene.VertexBuffer, com.jme3.opencl.MemoryAccess) }.
+     * This method must be called before the buffer is used. After the work is
+     * done, the buffer must be released by calling
+     * {@link #releaseBufferForSharingAsync(com.jme3.opencl.CommandQueue) }
+     * so that OpenGL can use the VertexBuffer again.
+     * @param queue the command queue
+     * @return the event object
+     */
+    public abstract Event acquireBufferForSharingAsync(CommandQueue queue);
+    
+    /**
+     * Aquires this buffer object for using. Only call this method if this buffer
+     * represents a shared object from OpenGL, created with e.g.
+     * {@link Context#bindVertexBuffer(com.jme3.scene.VertexBuffer, com.jme3.opencl.MemoryAccess) }.
+     * This method must be called before the buffer is used. After the work is
+     * done, the buffer must be released by calling
+     * {@link #releaseBufferForSharingAsync(com.jme3.opencl.CommandQueue) }
+     * so that OpenGL can use the VertexBuffer again.
+     * 
+     * The generated event object is directly released.
+     * This brings a performance improvement when the resource is e.g. directly
+     * used by a kernel afterwards on the same queue (this implicitly waits for
+     * this action). If you need the event, use 
+     * {@link #acquireBufferForSharingAsync(com.jme3.opencl.CommandQueue) } instead.
+     * 
+     * @param queue the command queue
+     */
+    public void acquireBufferForSharingNoEvent(CommandQueue queue) {
+        //default implementation, overwrite for better performance
+        acquireBufferForSharingAsync(queue).release();
+    }
+    
+    /**
+     * Releases a shared buffer object.
+     * Call this method after the buffer object was acquired by
+     * {@link #acquireBufferForSharingAsync(com.jme3.opencl.CommandQueue) }
+     * to hand the control back to OpenGL.
+     * @param queue the command queue
+     * @return the event object
+     */
+    public abstract Event releaseBufferForSharingAsync(CommandQueue queue);
+    
+    /**
+     * Releases a shared buffer object.
+     * Call this method after the buffer object was acquired by
+     * {@link #acquireBufferForSharingAsync(com.jme3.opencl.CommandQueue) }
+     * to hand the control back to OpenGL.
+     * The generated event object is directly released, resulting in 
+     * performance improvements.
+     * @param queue the command queue
+     */
+    public void releaseBufferForSharingNoEvent(CommandQueue queue) {
+        //default implementation, overwrite for better performance
+        releaseBufferForSharingAsync(queue).release();
+    }
+    
+}

+ 68 - 0
jme3-core/src/main/java/com/jme3/opencl/CommandQueue.java

@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl;
+
+/**
+ * Wrapper for an OpenCL command queue.
+ * The command queue serializes every GPU function call: By passing the same
+ * queue to OpenCL function (buffer, image operations, kernel calls), it is 
+ * ensured that they are executed in the order in which they are passed.
+ * <br>
+ * Each command queue is associtated with exactly one device: that device
+ * is specified on creation ({@link Context#createQueue(com.jme3.opencl.Device) })
+ * and all commands are sent to this device.
+ * @author shaman
+ */
+public abstract class CommandQueue extends AbstractOpenCLObject {
+
+    protected CommandQueue(ObjectReleaser releaser) {
+        super(releaser);
+    }
+    
+    /**
+     * Issues all previously queued OpenCL commands in command_queue to the
+     * device associated with command queue. Flush only guarantees that all
+     * queued commands to command_queue will eventually be submitted to the
+     * appropriate device. There is no guarantee that they will be complete
+     * after flush returns.
+     */
+    public abstract void flush();
+
+    /**
+     * Blocks until all previously queued OpenCL commands in command queue are
+     * issued to the associated device and have completed. Finish does not
+     * return until all previously queued commands in command queue have been
+     * processed and completed. Finish is also a synchronization point.
+     */
+    public abstract void finish();
+
+}

+ 439 - 0
jme3-core/src/main/java/com/jme3/opencl/Context.java

@@ -0,0 +1,439 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.AssetNotFoundException;
+import com.jme3.opencl.Image.ImageDescriptor;
+import com.jme3.opencl.Image.ImageFormat;
+import com.jme3.opencl.Image.ImageType;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Texture;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * The central OpenCL context. Every action starts from here.
+ * The context can be obtained by {@link com.jme3.system.JmeContext#getOpenCLContext() }.
+ * <p>
+ * The context is used to:
+ * <ul>
+ *  <li>Query the available devices</li>
+ *  <li>Create a command queue</li>
+ *  <li>Create buffers and images</li>
+ *  <li>Created buffers and images shared with OpenGL vertex buffers, textures and renderbuffers</li>
+ *  <li>Create program objects from source code and source files</li>
+ * </ul>
+ * @author shaman
+ */
+public abstract class Context extends AbstractOpenCLObject {
+    private static final Logger LOG = Logger.getLogger(Context.class.getName());
+
+    protected Context(ObjectReleaser releaser) {
+        super(releaser);
+    }
+
+    /**
+     * Returns all available devices for this context.
+     * These devices all belong to the same {@link Platform}.
+     * They are used to create a command queue sending commands to a particular
+     * device, see {@link #createQueue(com.jme3.opencl.Device) }.
+     * Also, device capabilities, like the supported OpenCL version, extensions,
+     * memory size and so on, are queried over the Device instances.
+     * <br>
+     * The available devices were specified by a {@link PlatformChooser}.
+     * @return 
+     */
+    public abstract List<? extends Device> getDevices();
+
+    /**
+     * Alternative version of {@link #createQueue(com.jme3.opencl.Device) },
+     * just uses the first device returned by {@link #getDevices() }.
+     * @return the command queue
+     */
+    public CommandQueue createQueue() {
+        return createQueue(getDevices().get(0));
+    }
+    /**
+     * Creates a command queue sending commands to the specified device.
+     * The device must be an entry of {@link #getDevices() }.
+     * @param device the target device
+     * @return the command queue
+     */
+	public abstract CommandQueue createQueue(Device device);
+
+    /**
+     * Allocates a new buffer of the specific size and access type on the device.
+     * @param size the size of the buffer in bytes
+     * @param access the allowed access of this buffer from kernel code
+     * @return the new buffer
+     */
+    public abstract Buffer createBuffer(long size, MemoryAccess access);
+    /**
+     * Alternative version of {@link #createBuffer(long, com.jme3.opencl.MemoryAccess) },
+     * creates a buffer with read and write access.
+     * @param size the size of the buffer in bytes
+     * @return the new buffer
+     */
+    public Buffer createBuffer(long size) {
+        return createBuffer(size, MemoryAccess.READ_WRITE);
+    }
+
+    /**
+     * Creates a new buffer wrapping the specific host memory. This host memory
+     * specified by a ByteBuffer can then be used directly by kernel code,
+     * although the access might be slower than with native buffers
+     * created by {@link #createBuffer(long, com.jme3.opencl.MemoryAccess) }.
+     * @param data the host buffer to use
+     * @param access the allowed access of this buffer from kernel code
+     * @return the new buffer
+     */
+    public abstract Buffer createBufferFromHost(ByteBuffer data, MemoryAccess access);
+    /**
+     * Alternative version of {@link #createBufferFromHost(java.nio.ByteBuffer, com.jme3.opencl.MemoryAccess) },
+     * creates a buffer with read and write access.
+     * @param data the host buffer to use
+     * @return the new buffer
+     */
+    public Buffer createBufferFromHost(ByteBuffer data) {
+        return createBufferFromHost(data, MemoryAccess.READ_WRITE);
+    }
+
+    /**
+     * Creates a new 1D, 2D, 3D image.<br>
+     * {@code ImageFormat} specifies the element type and order, like RGBA of floats.<br>
+     * {@code ImageDescriptor} specifies the dimension of the image.<br>
+     * Furthermore, a ByteBuffer can be specified in the ImageDescriptor together
+     * with row and slice pitches. This buffer is then used to store the image.
+     * If no ByteBuffer is specified, a new buffer is allocated (this is the
+     * normal behaviour).
+     * @param access the allowed access of this image from kernel code
+     * @param format the image format
+     * @param descr the image descriptor
+     * @return the new image object
+     */
+    public abstract Image createImage(MemoryAccess access, ImageFormat format, ImageDescriptor descr);
+	//TODO: add simplified methods for 1D, 2D, 3D textures
+    
+    /**
+     * Queries all supported image formats for a specified memory access and
+     * image type.
+     * <br>
+     * Note that the returned array may contain {@code ImageFormat} objects
+     * where {@code ImageChannelType} or {@code ImageChannelOrder} are {@code null}
+     * (or both). This is the case when the device supports new formats that
+     * are not included in this wrapper yet.
+     * @param access the memory access type
+     * @param type the image type (1D, 2D, 3D, ...)
+     * @return an array of all supported image formats
+     */
+    public abstract ImageFormat[] querySupportedFormats(MemoryAccess access, ImageType type);
+    
+	//Interop
+    /**
+     * Creates a shared buffer from a VertexBuffer. 
+     * The returned buffer and the vertex buffer operate on the same memory, 
+     * changes in one view are visible in the other view.
+     * This can be used to modify meshes directly from OpenCL (e.g. for particle systems).
+     * <br>
+     * <b>Note:</b> The vertex buffer must already been uploaded to the GPU,
+     * i.e. it must be used at least once for drawing.
+     * <p>
+     * Before the returned buffer can be used, it must be acquried explicitly
+     * by {@link Buffer#acquireBufferForSharingAsync(com.jme3.opencl.CommandQueue) }
+     * and after modifying it, released by {@link Buffer#releaseBufferForSharingAsync(com.jme3.opencl.CommandQueue) }.
+     * This is needed so that OpenGL and OpenCL operations do not interfer with each other.
+     * @param vb the vertex buffer to share
+     * @param access the memory access for the kernel
+     * @return the new buffer
+     */
+    public abstract Buffer bindVertexBuffer(VertexBuffer vb, MemoryAccess access);
+
+    /**
+     * Creates a shared image object from a jME3-image.
+     * The returned image shares the same memory with the jME3-image, changes
+     * in one view are visible in the other view.
+     * This can be used to modify textures and images directly from OpenCL
+     * (e.g. for post processing effects and other texture effects).
+     * <br>
+     * <b>Note:</b> The image must already been uploaded to the GPU,
+     * i.e. it must be used at least once for drawing.
+     * <p>
+     * Before the returned image can be used, it must be acquried explicitly
+     * by {@link Image#acquireImageForSharingAsync(com.jme3.opencl.CommandQueue) }
+     * and after modifying it, released by {@link Image#releaseImageForSharingAsync(com.jme3.opencl.CommandQueue) }
+     * This is needed so that OpenGL and OpenCL operations do not interfer with each other.
+     * 
+     * @param image the jME3 image object
+     * @param textureType the texture type (1D, 2D, 3D), since this is not stored in the image
+     * @param miplevel the mipmap level that should be shared
+     * @param access the allowed memory access for kernels
+     * @return the OpenCL image
+     */
+    public abstract Image bindImage(com.jme3.texture.Image image, Texture.Type textureType, int miplevel, MemoryAccess access);
+    /**
+     * Creates a shared image object from a jME3 texture.
+     * The returned image shares the same memory with the jME3 texture, changes
+     * in one view are visible in the other view.
+     * This can be used to modify textures and images directly from OpenCL
+     * (e.g. for post processing effects and other texture effects).
+     * <br>
+     * <b>Note:</b> The image must already been uploaded to the GPU,
+     * i.e. it must be used at least once for drawing.
+     * <p>
+     * Before the returned image can be used, it must be acquried explicitly
+     * by {@link Image#acquireImageForSharingAsync(com.jme3.opencl.CommandQueue) }
+     * and after modifying it, released by {@link Image#releaseImageForSharingAsync(com.jme3.opencl.CommandQueue) }
+     * This is needed so that OpenGL and OpenCL operations do not interfer with each other.
+     * <p>
+     * This method is equivalent to calling
+     * {@code bindImage(texture.getImage(), texture.getType(), miplevel, access)}.
+     * 
+     * @param texture the jME3 texture
+     * @param miplevel the mipmap level that should be shared
+     * @param access the allowed memory access for kernels
+     * @return the OpenCL image
+     */
+    public Image bindImage(Texture texture, int miplevel, MemoryAccess access) {
+        return bindImage(texture.getImage(), texture.getType(), miplevel, access);
+    }
+    /**
+     * Alternative version to {@link #bindImage(com.jme3.texture.Texture, int, com.jme3.opencl.MemoryAccess) },
+     * uses {@code miplevel=0}. 
+     * @param texture the jME3 texture
+     * @param access the allowed memory access for kernels
+     * @return the OpenCL image
+     */
+    public Image bindImage(Texture texture, MemoryAccess access) {
+        return bindImage(texture, 0, access);
+    }
+    /**
+     * Creates a shared image object from a jME3 render buffer.
+     * The returned image shares the same memory with the jME3 render buffer, changes
+     * in one view are visible in the other view.
+     * <br>
+     * This can be used as an alternative to post processing effects
+     * (e.g. reduce sum operations, needed e.g. for tone mapping).
+     * <br>
+     * <b>Note:</b> The renderbuffer must already been uploaded to the GPU,
+     * i.e. it must be used at least once for drawing.
+     * <p>
+     * Before the returned image can be used, it must be acquried explicitly
+     * by {@link Image#acquireImageForSharingAsync(com.jme3.opencl.CommandQueue) }
+     * and after modifying it, released by {@link Image#releaseImageForSharingAsync(com.jme3.opencl.CommandQueue) }
+     * This is needed so that OpenGL and OpenCL operations do not interfer with each other.
+     * 
+     * @param buffer
+     * @param access
+     * @return 
+     */
+    public Image bindRenderBuffer(FrameBuffer.RenderBuffer buffer, MemoryAccess access) {
+        if (buffer.getTexture() == null) {
+            return bindPureRenderBuffer(buffer, access);
+        } else {
+            return bindImage(buffer.getTexture(), access);
+        }
+    }
+    protected abstract Image bindPureRenderBuffer(FrameBuffer.RenderBuffer buffer, MemoryAccess access);
+
+    /**
+     * Creates a program object from the provided source code.
+     * The program still needs to be compiled using {@link Program#build() }.
+     * 
+     * @param sourceCode the source code
+     * @return the program object
+     */
+    public abstract Program createProgramFromSourceCode(String sourceCode);
+    
+    /**
+     * Resolves dependencies (using {@code #include } in the source code)
+     * and delegates the combined source code to
+     * {@link #createProgramFromSourceCode(java.lang.String) }.
+     * Important: only absolute paths are allowed.
+     * @param sourceCode the original source code
+     * @param assetManager the asset manager to load the files
+     * @return the created program object
+     * @throws AssetNotFoundException if a dependency could not be loaded
+     */
+    public Program createProgramFromSourceCodeWithDependencies(String sourceCode, AssetManager assetManager) {
+        StringBuilder builder = new StringBuilder(sourceCode.length());
+        BufferedReader reader = new BufferedReader(new StringReader(sourceCode));
+        try {
+            buildSourcesRec(reader, builder, assetManager);
+        } catch (IOException ex) {
+            throw new AssetNotFoundException("Unable to read a dependency file", ex);
+        }
+        return createProgramFromSourceCode(builder.toString());
+    }
+    private void buildSourcesRec(BufferedReader reader, StringBuilder builder, AssetManager assetManager) throws IOException {
+        String ln;
+        while ((ln = reader.readLine()) != null) {
+            if (ln.trim().startsWith("#import ")) {
+                ln = ln.trim().substring(8).trim();
+                if (ln.startsWith("\"")) {
+                    ln = ln.substring(1);
+                }
+                if (ln.endsWith("\"")) {
+                    ln = ln.substring(0, ln.length()-1);
+                }
+                AssetInfo info = assetManager.locateAsset(new AssetKey<String>(ln));
+                if (info == null) {
+                    throw new AssetNotFoundException("Unable to load source file \""+ln+"\"");
+                }
+                try (BufferedReader r = new BufferedReader(new InputStreamReader(info.openStream()))) {
+                    builder.append("//-- begin import ").append(ln).append(" --\n");
+                    buildSourcesRec(r, builder, assetManager);
+                    builder.append("//-- end import ").append(ln).append(" --\n");
+                }
+            } else {
+                builder.append(ln).append('\n');
+            }
+        }
+    }
+    
+    /**
+     * Creates a program object from the provided source code and files.
+     * The source code is made up from the specified include string first, 
+     * then all files specified by the resource array (array of asset paths)
+     * are loaded by the provided asset manager and appended to the source code.
+     * <p>
+     * The typical use case is:
+     * <ul>
+     *  <li>The include string contains some compiler constants like the grid size </li>
+     *  <li>Some common OpenCL files used as libraries (Convention: file names end with {@code .clh}</li>
+     *  <li>One main OpenCL file containing the actual kernels (Convention: file name ends with {@code .cl})</li>
+     * </ul>
+     * 
+     * After the files were combined, additional include statements are resolved
+     * by {@link #createProgramFromSourceCodeWithDependencies(java.lang.String, com.jme3.asset.AssetManager) }.
+     * 
+     * @param assetManager the asset manager used to load the files
+     * @param include an additional include string
+     * @param resources an array of asset paths pointing to OpenCL source files
+     * @return the new program objects
+     * @throws AssetNotFoundException if a file could not be loaded
+     */
+    public Program createProgramFromSourceFilesWithInclude(AssetManager assetManager, String include, String... resources) {
+        return createProgramFromSourceFilesWithInclude(assetManager, include, Arrays.asList(resources));
+    }
+
+    /**
+     * Creates a program object from the provided source code and files.
+     * The source code is made up from the specified include string first, 
+     * then all files specified by the resource array (array of asset paths)
+     * are loaded by the provided asset manager and appended to the source code.
+     * <p>
+     * The typical use case is:
+     * <ul>
+     *  <li>The include string contains some compiler constants like the grid size </li>
+     *  <li>Some common OpenCL files used as libraries (Convention: file names end with {@code .clh}</li>
+     *  <li>One main OpenCL file containing the actual kernels (Convention: file name ends with {@code .cl})</li>
+     * </ul>
+     * 
+     * After the files were combined, additional include statements are resolved
+     * by {@link #createProgramFromSourceCodeWithDependencies(java.lang.String, com.jme3.asset.AssetManager) }.
+     * 
+     * @param assetManager the asset manager used to load the files
+     * @param include an additional include string
+     * @param resources an array of asset paths pointing to OpenCL source files
+     * @return the new program objects
+     * @throws AssetNotFoundException if a file could not be loaded
+     */
+    public Program createProgramFromSourceFilesWithInclude(AssetManager assetManager, String include, List<String> resources) {
+        StringBuilder str = new StringBuilder();
+        str.append(include);
+        for (String res : resources) {
+            AssetInfo info = assetManager.locateAsset(new AssetKey<String>(res));
+            if (info == null) {
+                throw new AssetNotFoundException("Unable to load source file \""+res+"\"");
+            }
+            try (BufferedReader reader = new BufferedReader(new InputStreamReader(info.openStream()))) {
+                while (true) {
+                    String line = reader.readLine();
+                    if (line == null) {
+                        break;
+                    }
+                    str.append(line).append('\n');
+                }
+            } catch (IOException ex) {
+                LOG.log(Level.WARNING, "unable to load source file '"+res+"'", ex);
+            }
+        }
+        return createProgramFromSourceCodeWithDependencies(str.toString(), assetManager);
+    }
+
+    /**
+     * Alternative version of {@link #createProgramFromSourceFilesWithInclude(com.jme3.asset.AssetManager, java.lang.String, java.lang.String...) }
+     * with an empty include string
+     * @throws AssetNotFoundException if a file could not be loaded
+     */
+    public Program createProgramFromSourceFiles(AssetManager assetManager, String... resources) {
+        return createProgramFromSourceFilesWithInclude(assetManager, "", resources);
+    }
+
+    /**
+     * Alternative version of {@link #createProgramFromSourceFilesWithInclude(com.jme3.asset.AssetManager, java.lang.String, java.util.List) }
+     * with an empty include string
+     * @throws AssetNotFoundException if a file could not be loaded
+     */
+    public Program createProgramFromSourceFiles(AssetManager assetManager, List<String> resources) {
+        return createProgramFromSourceFilesWithInclude(assetManager, "", resources);
+    }
+    
+    /**
+     * Creates a program from the specified binaries.
+     * The binaries are created by {@link Program#getBinary(com.jme3.opencl.Device) }.
+     * The returned program still needs to be build using
+     * {@link Program#build(java.lang.String, com.jme3.opencl.Device...) }.
+     * <b>Important:</b>The device passed to {@code Program.getBinary(..)},
+     * this method and {@code Program#build(..)} must be the same.
+     * 
+     * The binaries are used to build a program cache across multiple launches
+     * of the application. The programs build mach faster from binaries than
+     * from sources.
+     * 
+     * @param binaries the binaries
+     * @param device the device to use
+     * @return the new program
+     */
+    public abstract Program createProgramFromBinary(ByteBuffer binaries, Device device);
+}

+ 91 - 0
jme3-core/src/main/java/com/jme3/opencl/DefaultPlatformChooser.java

@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ * A default implementation of {@link PlatformChooser}.
+ * It favors GPU devices with OpenGL sharing, then any devices with OpenGL sharing,
+ * then any possible device.
+ * @author shaman
+ */
+public class DefaultPlatformChooser implements PlatformChooser {
+    private static final Logger LOG = Logger.getLogger(DefaultPlatformChooser.class.getName());
+
+    @Override
+    public List<? extends Device> chooseDevices(List<? extends Platform> platforms) {
+        ArrayList<Device> result = new ArrayList<Device>();
+        for (Platform p : platforms) {
+            if (!p.hasOpenGLInterop()) {
+                continue; //must support interop
+            }
+            for (Device d : p.getDevices()) {
+                if (d.hasOpenGLInterop() && d.getDeviceType()==Device.DeviceType.GPU) {
+                    result.add(d); //GPU prefered
+                }
+            }
+            if (!result.isEmpty()) {
+                return result;
+            }
+        }
+        //no GPU devices found, try all
+        for (Platform p : platforms) {
+            if (!p.hasOpenGLInterop()) {
+                continue; //must support interop
+            }
+            for (Device d : p.getDevices()) {
+                if (d.hasOpenGLInterop()) {
+                    result.add(d); //just interop needed
+                }
+            }
+            if (!result.isEmpty()) {
+                return result;
+            }
+        }
+        //still no one found, try without interop
+        LOG.warning("No device with OpenCL-OpenGL-interop found, try without");
+        for (Platform p : platforms) {
+            for (Device d : p.getDevices()) {
+                result.add(d);
+            }
+            if (!result.isEmpty()) {
+                return result;
+            }
+        }
+        //no devices available at all!
+        return result; //result is empty
+    }
+    
+}

+ 311 - 0
jme3-core/src/main/java/com/jme3/opencl/Device.java

@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl;
+
+import java.util.Collection;
+
+/**
+ * Represents a hardware device actually running the OpenCL kernels.
+ * A {@link Context} can be accociated with multiple {@code Devices}
+ * that all belong to the same {@link Platform}.
+ * For execution, a single device must be chosen and passed to a command
+ * queue ({@link Context#createQueue(com.jme3.opencl.Device) }).
+ * <p>
+ * This class is used to query the capabilities of the underlying device.
+ * 
+ * @author shaman
+ */
+public interface Device {
+	
+    /**
+     * @return the platform accociated with this device
+     */
+    Platform getPlatform();
+    
+    /**
+     * The device type
+     */
+	public static enum DeviceType {
+		DEFAULT,
+		CPU,
+		GPU,
+		ACCELEARTOR,
+		ALL
+	}
+    /**
+     * @return queries the device type
+     */
+	DeviceType getDeviceType();
+    /**
+     * @return the vendor id
+     */
+	int getVendorId();
+    /**
+     * checks if this device is available at all, must always be tested
+     * @return checks if this device is available at all, must always be tested
+     */
+	boolean isAvailable();
+	
+    /**
+     * @return if this device has a compiler for kernel code
+     */
+	boolean hasCompiler();
+    /**
+     * @return supports double precision floats (64 bit)
+     */
+	boolean hasDouble();
+    /**
+     * @return supports half precision floats (16 bit)
+     */
+	boolean hasHalfFloat();
+    /**
+     * @return supports error correction for every access to global or constant memory
+     */
+	boolean hasErrorCorrectingMemory();
+    /**
+     * @return supports unified virtual memory (OpenCL 2.0)
+     */
+	boolean hasUnifiedMemory();
+    /**
+     * @return supports images
+     */
+	boolean hasImageSupport();
+    /**
+     * @return supports writes to 3d images (this is an extension)
+     */
+    boolean hasWritableImage3D();
+    /**
+     * @return supports sharing with OpenGL
+     */
+    boolean hasOpenGLInterop();
+    /**
+     * Explicetly tests for the availability of the specified extension
+     * @param extension the name of the extension
+     * @return {@code true} iff this extension is supported
+     */
+	boolean hasExtension(String extension);
+    /**
+     * Lists all available extensions
+     * @return all available extensions
+     */
+	Collection<? extends String> getExtensions();
+	
+    /**
+     * Returns the number of parallel compute units on
+     * the OpenCL device. A work-group
+     * executes on a single compute unit. The
+     * minimum value is 1.
+     * @return the number of parallel compute units
+     * @see #getMaximumWorkItemDimensions() 
+     * @see #getMaximumWorkItemSizes() 
+     */
+	int getComputeUnits();
+    /**
+     * @return maximum clock frequency of the device in MHz
+     */
+	int getClockFrequency();
+    /**
+     * Returns the default compute device address space
+     * size specified as an unsigned integer value
+     * in bits. Currently supported values are 32
+     * or 64 bits.
+     * @return the size of an adress
+     */
+	int getAddressBits();
+    /**
+     * @return {@code true} if this device is little endian
+     */
+	boolean isLittleEndian();
+	
+    /**
+     * The maximum dimension that specify the local and global work item ids.
+     * You can always assume to be this at least 3.
+     * Therefore, the ids are always three integers x,y,z.
+     * @return the maximum dimension of work item ids
+     */
+	long getMaximumWorkItemDimensions();
+    /**
+     * Maximum number of work-items that can be specified in each dimension of the
+     * work-group to {@link Kernel#Run2(com.jme3.opencl.CommandQueue, com.jme3.opencl.WorkSize, com.jme3.opencl.WorkSize, java.lang.Object...) }.
+     * The array has a length of at least 3.
+     * @return the maximum size of the work group in each dimension
+     */
+	long[] getMaximumWorkItemSizes();
+    /**
+     * Maximum number of work-items in a
+     * work-group executing a kernel on a single
+     * compute unit, using the data parallel
+     * execution model.
+     * @return maximum number of work-items in a work-group
+     */
+	long getMaxiumWorkItemsPerGroup();
+	
+    /**
+     * @return the maximum number of samples that can be used in a kernel
+     */
+	int getMaximumSamplers();
+    /**
+     * @return the maximum number of images that can be used for reading in a kernel
+     */
+	int getMaximumReadImages();
+    /**
+     * @return the maximum number of images that can be used for writing in a kernel
+     */
+	int getMaximumWriteImages();
+    /**
+     * Queries the maximal size of a 2D image
+     * @return an array of length 2 with the maximal size of a 2D image
+     */
+	long[] getMaximumImage2DSize();
+    /**
+     * Queries the maximal size of a 3D image
+     * @return an array of length 3 with the maximal size of a 3D image
+     */
+	long[] getMaximumImage3DSize();
+	
+    /**
+     * @return the maximal size of a memory object (buffer and image) in bytes
+     */
+    long getMaximumAllocationSize();
+    /**
+     * @return the total available global memory in bytes
+     */
+    long getGlobalMemorySize();
+    /**
+     * @return the total available local memory in bytes
+     */
+    long getLocalMemorySize();
+    /**
+     * Returns the maximal size of a constant buffer.
+     * <br>
+     * Constant buffers are normal buffer objects, but passed to the kernel
+     * with the special declaration {@code __constant BUFFER_TYPE* BUFFER_NAME}.
+     * Because they have a special caching, their size is usually very limited.
+     * 
+     * @return the maximal size of a constant buffer
+     */
+    long getMaximumConstantBufferSize();
+    /**
+     * @return the maximal number of constant buffer arguments in a kernel call
+     */
+    int getMaximumConstantArguments();
+    
+	//TODO: cache, prefered sizes properties
+    /**
+     * OpenCL profile string. Returns the profile name supported by the device.
+     * The profile name returned can be one of the following strings:<br>
+     * FULL_PROFILE – if the device supports the OpenCL specification
+     * (functionality defined as part of the core specification and does not
+     * require any extensions to be supported).<br>
+     * EMBEDDED_PROFILE - if the device supports the OpenCL embedded profile.
+     *
+     * @return the profile string
+     */
+	String getProfile();
+    /**
+     * OpenCL version string. Returns the OpenCL version supported by the
+     * device. This version string has the following format: OpenCL space
+     * major_version.minor_version space vendor-specific information.
+     * <br>
+     * E.g. OpenCL 1.1, OpenCL 1.2, OpenCL 2.0
+     *
+     * @return the version string
+     */
+	String getVersion();
+    /**
+     * Extracts the major version from the version string
+     * @return the major version
+     * @see #getVersion() 
+     */
+	int getVersionMajor();
+    /**
+     * Extracts the minor version from the version string
+     * @return the minor version
+     * @see #getVersion() }
+     */
+	int getVersionMinor();
+    
+    /**
+     * OpenCL C version string. Returns the highest OpenCL C version supported
+     * by the compiler for this device that is not of type
+     * CL_DEVICE_TYPE_CUSTOM. This version string has the following format:
+     * OpenCL space C space major_version.minor_version space vendor-specific
+     * information.<br>
+     * The major_version.minor_version value returned must be 1.2 if
+     * CL_DEVICE_VERSION is OpenCL 1.2. The major_version.minor_version value
+     * returned must be 1.1 if CL_DEVICE_VERSION is OpenCL 1.1. The
+     * major_version.minor_version value returned can be 1.0 or 1.1 if
+     * CL_DEVICE_VERSION is OpenCL 1.0.
+     *
+     * @return the compiler version
+     */
+	String getCompilerVersion();
+    /**
+     * Extracts the major version from the compiler version
+     * @return the major compiler version
+     * @see #getCompilerVersion() 
+     */
+	int getCompilerVersionMajor();
+    /**
+     * Extracts the minor version from the compiler version
+     * @return the minor compiler version
+     * @see #getCompilerVersion() 
+     */
+	int getCompilerVersionMinor();
+    /**     
+     * @return the OpenCL software driver version string in the form
+     * major_number.minor_number
+     */
+	String getDriverVersion();
+    /**
+     * Extracts the major version from the driver version
+     * @return the major driver version
+     * @see #getDriverVersion() 
+     */
+	int getDriverVersionMajor();
+    /**
+     * Extracts the minor version from the driver version
+     * @return the minor driver version
+     * @see #getDriverVersion() 
+     */
+	int getDriverVersionMinor();
+    
+    /**
+     * @return the device name
+     */
+	String getName();
+    /**
+     * @return the vendor
+     */
+	String getVendor();
+
+}

+ 59 - 0
jme3-core/src/main/java/com/jme3/opencl/Event.java

@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl;
+
+/**
+ * Wrapper for an OpenCL Event object.
+ * Events are returned from kernel launches and all asynchronous operations.
+ * They allow to test if the action has completed and to block until the operation
+ * is done.
+ * @author shaman
+ */
+public abstract class Event extends AbstractOpenCLObject {
+
+    protected Event(ObjectReleaser releaser) {
+        super(releaser);
+    }
+	
+    /**
+     * Waits until the action has finished (blocking).
+     * This automatically releases the event.
+     */
+	public abstract void waitForFinished();
+	
+    /**
+     * Tests if the action is completed.
+     * If the action is completed, the event is released.
+     * @return {@code true} if the action is completed
+     */
+	public abstract boolean isCompleted();
+}

+ 537 - 0
jme3-core/src/main/java/com/jme3/opencl/Image.java

@@ -0,0 +1,537 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl;
+
+import com.jme3.math.ColorRGBA;
+import java.nio.ByteBuffer;
+import java.util.Objects;
+
+/**
+ * Wrapper for an OpenCL image.
+ * <br>
+ * An image object is similar to a {@link Buffer}, but with a specific element
+ * format and buffer structure.
+ * <br>
+ * The image is specified by the {@link ImageDescriptor}, specifying 
+ * the extend and dimension of the image, and {@link ImageFormat}, specifying
+ * the type of each pixel.
+ * <br>
+ * An image is created from scratch using 
+ * {@link Context#createImage(com.jme3.opencl.MemoryAccess, com.jme3.opencl.Image.ImageFormat, com.jme3.opencl.Image.ImageDescriptor) }
+ * or from OpenGL by
+ * {@link Context#bindImage(com.jme3.texture.Image, com.jme3.texture.Texture.Type, int, com.jme3.opencl.MemoryAccess) }
+ * (and alternative versions).
+ * 
+ * <p>
+ * Most methods take long arrays as input: {@code long[] origin} and {@code long[] region}.
+ * Both are arrays of length 3.
+ * <br>
+ * <b>origin</b> defines the (x, y, z) offset in pixels in the 1D, 2D or 3D
+ * image, the (x, y) offset and the image index in the 2D image array or the (x)
+ * offset and the image index in the 1D image array. If image is a 2D image
+ * object, origin[2] must be 0. If image is a 1D image or 1D image buffer
+ * object, origin[1] and origin[2] must be 0. If image is a 1D image array
+ * object, origin[2] must be 0. If image is a 1D image array object, origin[1]
+ * describes the image index in the 1D image array. If image is a 2D image array
+ * object, origin[2] describes the image index in the 2D image array.
+ * <br>
+ * <b>region</b> defines the (width, height, depth) in pixels of the 1D, 2D or
+ * 3D rectangle, the (width, height) in pixels of the 2D rectangle and the
+ * number of images of a 2D image array or the (width) in pixels of the 1D
+ * rectangle and the number of images of a 1D image array. If image is a 2D
+ * image object, region[2] must be 1. If image is a 1D image or 1D image buffer
+ * object, region[1] and region[2] must be 1. If image is a 1D image array
+ * object, region[2] must be 1. The values in region cannot be 0.
+ *
+ * @author shaman
+ */
+public abstract class Image extends AbstractOpenCLObject {
+    
+    /**
+     * {@code ImageChannelType} describes the size of the channel data type.
+     */
+    public static enum ImageChannelType {
+        SNORM_INT8,
+        SNORM_INT16,
+        UNORM_INT8,
+        UNORM_INT16,
+        UNORM_SHORT_565,
+        UNORM_SHORT_555,
+        UNORM_INT_101010,
+        SIGNED_INT8,
+        SIGNED_INT16,
+        SIGNED_INT32,
+        UNSIGNED_INT8,
+        UNSIGNED_INT16,
+        UNSIGNED_INT32,
+        HALF_FLOAT,
+        FLOAT
+    }
+    
+    /**
+     * {@code ImageChannelOrder} specifies the number of channels and the channel layout i.e. the
+memory layout in which channels are stored in the image.
+     */
+    public static enum ImageChannelOrder {
+        R, Rx, A,
+        INTENSITY,
+        LUMINANCE,
+        RG, RGx, RA,
+        RGB, RGBx,
+        RGBA,
+        ARGB, BGRA
+    }
+
+    /**
+     * Describes the image format, consisting of 
+     * {@link ImageChannelOrder} and {@link ImageChannelType}.
+     */
+    public static class ImageFormat { //Struct
+        public ImageChannelOrder channelOrder;
+        public ImageChannelType channelType;
+
+        public ImageFormat() {
+        }
+
+        public ImageFormat(ImageChannelOrder channelOrder, ImageChannelType channelType) {
+            this.channelOrder = channelOrder;
+            this.channelType = channelType;
+        }
+
+        @Override
+        public String toString() {
+            return "ImageFormat{" + "channelOrder=" + channelOrder + ", channelType=" + channelType + '}';
+        }
+
+        @Override
+        public int hashCode() {
+            int hash = 5;
+            hash = 61 * hash + Objects.hashCode(this.channelOrder);
+            hash = 61 * hash + Objects.hashCode(this.channelType);
+            return hash;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == null) {
+                return false;
+            }
+            if (getClass() != obj.getClass()) {
+                return false;
+            }
+            final ImageFormat other = (ImageFormat) obj;
+            if (this.channelOrder != other.channelOrder) {
+                return false;
+            }
+            if (this.channelType != other.channelType) {
+                return false;
+            }
+            return true;
+        }
+        
+    }
+
+    /**
+     * The possible image types / dimensions.
+     */
+    public static enum ImageType {
+        IMAGE_1D,
+        IMAGE_1D_BUFFER,
+        IMAGE_2D,
+        IMAGE_3D,
+        IMAGE_1D_ARRAY,
+        IMAGE_2D_ARRAY
+    }
+
+    /**
+     * The image descriptor structure describes the type and dimensions of the image or image array.
+     * <p>
+     * There exists two constructors:<br>
+     * {@link #ImageDescriptor(com.jme3.opencl.Image.ImageType, long, long, long, long) }
+     * is used when an image with new memory should be created (used most often).<br>
+     * {@link #ImageDescriptor(com.jme3.opencl.Image.ImageType, long, long, long, long, long, long, java.nio.ByteBuffer) }
+     * creates an image using the provided {@code ByteBuffer} as source.
+     */
+    public static class ImageDescriptor { //Struct
+        public ImageType type;
+        public long width;
+        public long height;
+        public long depth;
+        public long arraySize;
+        public long rowPitch;
+        public long slicePitch;
+        public ByteBuffer hostPtr;
+        /*
+        public int numMipLevels;  //They must always be set to zero
+        public int numSamples;
+        */
+
+        public ImageDescriptor() {
+        }
+
+        /**
+         * Used to specify an image with the provided ByteBuffer as soruce
+         * @param type the image type
+         * @param width the width
+         * @param height the height, unused for image types {@code ImageType.IMAGE_1D*}
+         * @param depth the depth of the image, only used for image type {@code ImageType.IMAGE_3D}
+         * @param arraySize the number of array elements for image type {@code ImageType.IMAGE_1D_ARRAY} and {@code ImageType.IMAGE_2D_ARRAY}
+         * @param rowPitch the row pitch of the provided buffer
+         * @param slicePitch the slice pitch of the provided buffer
+         * @param hostPtr host buffer used as image memory
+         */
+        public ImageDescriptor(ImageType type, long width, long height, long depth, long arraySize, long rowPitch, long slicePitch, ByteBuffer hostPtr) {
+            this.type = type;
+            this.width = width;
+            this.height = height;
+            this.depth = depth;
+            this.arraySize = arraySize;
+            this.rowPitch = rowPitch;
+            this.slicePitch = slicePitch;
+            this.hostPtr = hostPtr;
+        }
+        /**
+         * Specifies an image without a host buffer, a new chunk of memory 
+         * will be allocated.
+         * @param type the image type
+         * @param width the width
+         * @param height the height, unused for image types {@code ImageType.IMAGE_1D*}
+         * @param depth the depth of the image, only used for image type {@code ImageType.IMAGE_3D}
+         * @param arraySize the number of array elements for image type {@code ImageType.IMAGE_1D_ARRAY} and {@code ImageType.IMAGE_2D_ARRAY}
+         */
+        public ImageDescriptor(ImageType type, long width, long height, long depth, long arraySize) {
+            this.type = type;
+            this.width = width;
+            this.height = height;
+            this.depth = depth;
+            this.arraySize = arraySize;
+            this.rowPitch = 0;
+            this.slicePitch = 0;
+            hostPtr = null;
+        }
+
+        @Override
+        public String toString() {
+            return "ImageDescriptor{" + "type=" + type + ", width=" + width + ", height=" + height + ", depth=" + depth + ", arraySize=" + arraySize + ", rowPitch=" + rowPitch + ", slicePitch=" + slicePitch + '}';
+        }
+        
+    }
+
+    protected Image(ObjectReleaser releaser) {
+        super(releaser);
+    }
+    
+    /**
+     * @return the width of the image
+     */
+    public abstract long getWidth();
+    /**
+     * @return the height of the image
+     */
+    public abstract long getHeight();
+    /**
+     * @return the depth of the image
+     */
+    public abstract long getDepth();
+    /**
+     * @return the row pitch when the image was created from a host buffer
+     * @see ImageDescriptor#ImageDescriptor(com.jme3.opencl.Image.ImageType, long, long, long, long, long, long, java.nio.ByteBuffer) 
+     */
+    public abstract long getRowPitch();
+    /**
+     * @return the slice pitch when the image was created from a host buffer
+     * @see ImageDescriptor#ImageDescriptor(com.jme3.opencl.Image.ImageType, long, long, long, long, long, long, java.nio.ByteBuffer) 
+     */
+    public abstract long getSlicePitch();
+    /**
+     * @return the number of elements in the image array
+     * @see ImageType#IMAGE_1D_ARRAY
+     * @see ImageType#IMAGE_2D_ARRAY
+     */
+    public abstract long getArraySize();
+    /**
+     * @return the image format
+     */
+    public abstract ImageFormat getImageFormat();
+    /**
+     * @return the image type
+     */
+    public abstract ImageType getImageType();
+    /**
+     * @return the number of bytes per pixel
+     */
+    public abstract int getElementSize();
+    
+    /**
+     * Performs a blocking read of the image into the specified byte buffer.
+     * @param queue the command queue
+     * @param dest the target byte buffer
+     * @param origin the image origin location, see class description for the format
+     * @param region the copied region, see class description for the format
+     * @param rowPitch the row pitch of the target buffer, must be set to 0 if the image is 1D.
+     * If set to 0 for 2D and 3D image, the row pitch is calculated as {@code bytesPerElement * width}
+     * @param slicePitch the slice pitch of the target buffer, must be set to 0 for 1D and 2D images.
+     * If set to 0 for 3D images, the slice pitch is calculated as {@code rowPitch * height}
+     */
+    public abstract void readImage(CommandQueue queue, ByteBuffer dest, long[] origin, long[] region, long rowPitch, long slicePitch);
+    /**
+     * Performs an async/non-blocking read of the image into the specified byte buffer.
+     * @param queue the command queue
+     * @param dest the target byte buffer
+     * @param origin the image origin location, see class description for the format
+     * @param region the copied region, see class description for the format
+     * @param rowPitch the row pitch of the target buffer, must be set to 0 if the image is 1D.
+     * If set to 0 for 2D and 3D image, the row pitch is calculated as {@code bytesPerElement * width}
+     * @param slicePitch the slice pitch of the target buffer, must be set to 0 for 1D and 2D images.
+     * If set to 0 for 3D images, the slice pitch is calculated as {@code rowPitch * height}
+     * @return the event object indicating the status of the operation
+     */
+    public abstract Event readImageAsync(CommandQueue queue, ByteBuffer dest, long[] origin, long[] region, long rowPitch, long slicePitch);
+    
+    /**
+     * Performs a blocking write from the specified byte buffer into the image.
+     * @param queue the command queue
+     * @param src the source buffer
+     * @param origin the image origin location, see class description for the format
+     * @param region the copied region, see class description for the format
+     * @param rowPitch the row pitch of the target buffer, must be set to 0 if the image is 1D.
+     * If set to 0 for 2D and 3D image, the row pitch is calculated as {@code bytesPerElement * width}
+     * @param slicePitch the slice pitch of the target buffer, must be set to 0 for 1D and 2D images.
+     * If set to 0 for 3D images, the slice pitch is calculated as {@code rowPitch * height}
+     */
+    public abstract void writeImage(CommandQueue queue, ByteBuffer src, long[] origin, long[] region, long rowPitch, long slicePitch);
+    /**
+     * Performs an async/non-blocking write from the specified byte buffer into the image.
+     * @param queue the command queue
+     * @param src the source buffer
+     * @param origin the image origin location, see class description for the format
+     * @param region the copied region, see class description for the format
+     * @param rowPitch the row pitch of the target buffer, must be set to 0 if the image is 1D.
+     * If set to 0 for 2D and 3D image, the row pitch is calculated as {@code bytesPerElement * width}
+     * @param slicePitch the slice pitch of the target buffer, must be set to 0 for 1D and 2D images.
+     * If set to 0 for 3D images, the slice pitch is calculated as {@code rowPitch * height}
+     * @return the event object indicating the status of the operation
+     */
+    public abstract Event writeImageAsync(CommandQueue queue, ByteBuffer src, long[] origin, long[] region, long rowPitch, long slicePitch);
+    
+    /**
+     * Performs a blocking copy operation from one image to another.
+     * <b>Important:</b> Both images must have the same format!
+     * @param queue the command queue
+     * @param dest the target image
+     * @param srcOrigin the source image origin, see class description for the format
+     * @param destOrigin the target image origin, see class description for the format
+     * @param region the copied region, see class description for the format
+     */
+    public abstract void copyTo(CommandQueue queue, Image dest, long[] srcOrigin, long[] destOrigin, long[] region);
+    /**
+     * Performs an async/non-blocking copy operation from one image to another.
+     * <b>Important:</b> Both images must have the same format!
+     * @param queue the command queue
+     * @param dest the target image
+     * @param srcOrigin the source image origin, see class description for the format
+     * @param destOrigin the target image origin, see class description for the format
+     * @param region the copied region, see class description for the format
+     * @return the event object indicating the status of the operation
+     */
+    public abstract Event copyToAsync(CommandQueue queue, Image dest, long[] srcOrigin, long[] destOrigin, long[] region);
+    
+    /**
+     * Maps the image into host memory.
+     * The returned structure contains the mapped byte buffer and row and slice pitch.
+     * The event object is set to {@code null}, it is needed for the asnyc
+     * version {@link #mapAsync(com.jme3.opencl.CommandQueue, long[], long[], com.jme3.opencl.MappingAccess) }.
+     * @param queue the command queue
+     * @param origin the image origin, see class description for the format
+     * @param region the mapped region, see class description for the format
+     * @param access the allowed memory access to the mapped memory
+     * @return a structure describing the mapped memory
+     * @see #unmap(com.jme3.opencl.CommandQueue, com.jme3.opencl.Image.ImageMapping) 
+     */
+    public abstract ImageMapping map(CommandQueue queue, long[] origin, long[] region, MappingAccess access);
+    /**
+     * Non-blocking version of {@link #map(com.jme3.opencl.CommandQueue, long[], long[], com.jme3.opencl.MappingAccess) }.
+     * The returned structure contains the mapped byte buffer and row and slice pitch.
+     * The event object is used to detect when the mapped memory is available.
+     * @param queue the command queue
+     * @param origin the image origin, see class description for the format
+     * @param region the mapped region, see class description for the format
+     * @param access the allowed memory access to the mapped memory
+     * @return a structure describing the mapped memory
+     * @see #unmap(com.jme3.opencl.CommandQueue, com.jme3.opencl.Image.ImageMapping) 
+     */
+    public abstract ImageMapping mapAsync(CommandQueue queue, long[] origin, long[] region, MappingAccess access);
+    /**
+     * Unmaps the mapped memory
+     * @param queue the command queue
+     * @param mapping the mapped memory
+     */
+    public abstract void unmap(CommandQueue queue, ImageMapping mapping);
+    
+    /**
+     * Describes a mapped region of the image
+     */
+    public static class ImageMapping {
+        /**
+         * The raw byte buffer
+         */
+        public final ByteBuffer buffer;
+        /**
+         * The row pitch in bytes.
+         * This value is at least {@code bytesPerElement * width}
+         */
+        public final long rowPitch;
+        /**
+         * The slice pitch in bytes.
+         * This value is at least {@code rowPitch * height}
+         */
+        public final long slicePitch;
+        /**
+         * The event object used to detect when the memory is available.
+         * @see #mapAsync(com.jme3.opencl.CommandQueue, long[], long[], com.jme3.opencl.MappingAccess) 
+         */
+        public final Event event;
+
+        public ImageMapping(ByteBuffer buffer, long rowPitch, long slicePitch, Event event) {
+            this.buffer = buffer;
+            this.rowPitch = rowPitch;
+            this.slicePitch = slicePitch;
+            this.event = event;
+        }
+        public ImageMapping(ByteBuffer buffer, long rowPitch, long slicePitch) {
+            this.buffer = buffer;
+            this.rowPitch = rowPitch;
+            this.slicePitch = slicePitch;
+            this.event = null;
+        }
+        
+    }
+    
+    /**
+     * Fills the image with the specified color.
+     * Does <b>only</b> work if the image channel is {@link ImageChannelType#FLOAT}
+     * or {@link ImageChannelType#HALF_FLOAT}.
+     * @param queue the command queue
+     * @param origin the image origin, see class description for the format
+     * @param region the size of the region, see class description for the format
+     * @param color the color to fill
+     * @return an event object to detect for the completion
+     */
+    public abstract Event fillAsync(CommandQueue queue, long[] origin, long[] region, ColorRGBA color);
+    /**
+     * Fills the image with the specified color given as four integer variables.
+     * Does <b>not</b> work if the image channel is {@link ImageChannelType#FLOAT}
+     * or {@link ImageChannelType#HALF_FLOAT}.
+     * @param queue the command queue
+     * @param origin the image origin, see class description for the format
+     * @param region the size of the region, see class description for the format
+     * @param color the color to fill, must be an array of length 4
+     * @return an event object to detect for the completion
+     */
+    public abstract Event fillAsync(CommandQueue queue, long[] origin, long[] region, int[] color);
+    
+    /**
+     * Copies this image into the specified buffer, no format conversion is done.
+     * This is the dual function to 
+     * {@link Buffer#copyToImageAsync(com.jme3.opencl.CommandQueue, com.jme3.opencl.Image, long, long[], long[]) }.
+     * @param queue the command queue
+     * @param dest the target buffer
+     * @param srcOrigin the image origin, see class description for the format
+     * @param srcRegion the copied region, see class description for the format
+     * @param destOffset an offset into the target buffer
+     * @return the event object to detect the completion of the operation
+     */
+    public abstract Event copyToBufferAsync(CommandQueue queue, Buffer dest, long[] srcOrigin, long[] srcRegion, long destOffset);
+    
+    /**
+     * Aquires this image object for using. Only call this method if this image
+     * represents a shared object from OpenGL, created with e.g.
+     * {@link Context#bindImage(com.jme3.texture.Image, com.jme3.texture.Texture.Type, int, com.jme3.opencl.MemoryAccess) }
+     * or variations.
+     * This method must be called before the image is used. After the work is
+     * done, the image must be released by calling
+     * {@link #releaseImageForSharingAsync(com.jme3.opencl.CommandQueue)  }
+     * so that OpenGL can use the image/texture/renderbuffer again.
+     * @param queue the command queue
+     * @return the event object
+     */
+    public abstract Event acquireImageForSharingAsync(CommandQueue queue);
+    
+    /**
+     * Aquires this image object for using. Only call this method if this image
+     * represents a shared object from OpenGL, created with e.g.
+     * {@link Context#bindImage(com.jme3.texture.Image, com.jme3.texture.Texture.Type, int, com.jme3.opencl.MemoryAccess) }
+     * or variations.
+     * This method must be called before the image is used. After the work is
+     * done, the image must be released by calling
+     * {@link #releaseImageForSharingAsync(com.jme3.opencl.CommandQueue)  }
+     * so that OpenGL can use the image/texture/renderbuffer again.
+     * 
+     * The generated event object is directly released.
+     * This brings a performance improvement when the resource is e.g. directly
+     * used by a kernel afterwards on the same queue (this implicitly waits for
+     * this action). If you need the event, use 
+     * {@link #acquireImageForSharingAsync(com.jme3.opencl.CommandQueue) }.
+     * 
+     * @param queue the command queue
+     */
+    public void acquireImageForSharingNoEvent(CommandQueue queue) {
+        //Default implementation, overwrite for performance
+        acquireImageForSharingAsync(queue).release();
+    }
+    
+    /**
+     * Releases a shared image object.
+     * Call this method after the image object was acquired by
+     * {@link #acquireImageForSharingAsync(com.jme3.opencl.CommandQueue) }
+     * to hand the control back to OpenGL.
+     * @param queue the command queue
+     * @return the event object
+     */
+    public abstract Event releaseImageForSharingAsync(CommandQueue queue);
+    
+    /**
+     * Releases a shared image object.
+     * Call this method after the image object was acquired by
+     * {@link #acquireImageForSharingAsync(com.jme3.opencl.CommandQueue) }
+     * to hand the control back to OpenGL.
+     * The generated event object is directly released, resulting in 
+     * performance improvements.
+     * @param queue the command queue
+     */
+    public void releaseImageForSharingNoEvent(CommandQueue queue) {
+        //default implementation, overwrite it for performance improvements
+        releaseImageForSharingAsync(queue).release();
+    }
+    
+    //TODO: add variants of the above two methods that don't create the event object, but release the event immediately
+}

+ 628 - 0
jme3-core/src/main/java/com/jme3/opencl/Kernel.java

@@ -0,0 +1,628 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl;
+
+import com.jme3.math.*;
+import com.jme3.util.TempVars;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * Wrapper for an OpenCL kernel, a piece of executable code on the GPU.
+ * <p>
+ * Terminology:<br>
+ * A Kernel is executed in parallel. In total number of parallel threads, 
+ * called work items, are specified by the <i>global work size</i> (of type
+ * {@link WorkSize}. These threads are organized in a 1D, 2D or 3D grid
+ * (of coarse, this is only a logical view). Inside each kernel,
+ * the id of each thread (i.e. the index inside this grid) can be requested
+ * by {@code get_global_id(dimension)} with {@code dimension=0,1,2}.
+ * <br>
+ * Not all threads can always be executed in parallel because there simply might
+ * not be enough processor cores.
+ * Therefore, the concept of a <i>work group</i> is introduced. The work group
+ * specifies the actual number of threads that are executed in parallel.
+ * The maximal size of it can be queried by {@link Device#getMaxiumWorkItemsPerGroup() }.
+ * Again, the threads inside the work group can be organized in a 1D, 2D or 3D
+ * grid, but this is also just a logical view (specifying how the threads are
+ * indexed). 
+ * The work group is imporatant for another concept: <i> shared memory</i>
+ * Unlike the normal global or constant memory (passing a {@link Buffer} object
+ * as argument), shared memory can't be set from outside. Shared memory is
+ * allocated by the kernel and is only valid within the kernel. It is used
+ * to quickly share data between threads within a work group.
+ * The size of the shared memory is specified by setting an instance of
+ * {@link LocalMem} or {@link LocalMemPerElement} as argument.<br>
+ * Due to heavy register usage or other reasons, a kernel might not be able
+ * to utilize a whole work group. Therefore, the actual number of threads
+ * that can be executed in a work group can be queried by 
+ * {@link #getMaxWorkGroupSize(com.jme3.opencl.Device) }, which might differ from the 
+ * value returned from the Device.
+ * 
+ * <p>
+ * There are two ways to launch a kernel:<br>
+ * First, arguments and the work group sizes can be set in advance 
+ * ({@code setArg(index, ...)}, {@code setGlobalWorkSize(...)} and {@code setWorkGroupSize(...)}.
+ * Then a kernel is launched by {@link #Run(com.jme3.opencl.CommandQueue) }.<br>
+ * Second, two convenient functions are provided that set the arguments
+ * and work sizes in one call:
+ * {@link #Run1(com.jme3.opencl.CommandQueue, com.jme3.opencl.Kernel.WorkSize, java.lang.Object...) }
+ * and {@link #Run2(com.jme3.opencl.CommandQueue, com.jme3.opencl.Kernel.WorkSize, com.jme3.opencl.Kernel.WorkSize, java.lang.Object...) }.
+ * 
+ * @author shaman
+ * @see Program#createKernel(java.lang.String) 
+ */
+public abstract class Kernel extends AbstractOpenCLObject {
+    /**
+     * The current global work size
+     */
+    protected final WorkSize globalWorkSize;
+    /**
+     * The current local work size
+     */
+    protected final WorkSize workGroupSize;
+
+    protected Kernel(ObjectReleaser releaser) {
+        super(releaser);
+        this.globalWorkSize = new WorkSize(0);
+        this.workGroupSize = new WorkSize(0);
+    }
+
+    /**
+     * @return the name of the kernel as defined in the program source code
+     */
+    public abstract String getName();
+
+    /**
+     * @return the number of arguments
+     */
+    public abstract int getArgCount();
+
+    /**
+     * @return the current global work size
+     */
+    public WorkSize getGlobalWorkSize() {
+        return globalWorkSize;
+    }
+
+    /**
+     * Sets the global work size.
+     * @param ws the work size to set
+     */
+    public void setGlobalWorkSize(WorkSize ws) {
+        globalWorkSize.set(ws);
+    }
+
+    /**
+     * Sets the global work size to a 1D grid
+     * @param size the size in 1D
+     */
+    public void setGlobalWorkSize(int size) {
+        globalWorkSize.set(1, size);
+    }
+
+    /**
+     * Sets the global work size to be a 2D grid
+     * @param width the width
+     * @param height the height
+     */
+    public void setGlobalWorkSize(int width, int height) {
+        globalWorkSize.set(2, width, height);
+    }
+
+    /**
+     * Sets the global work size to be a 3D grid
+     * @param width the width
+     * @param height the height
+     * @param depth the depth
+     */
+    public void setGlobalWorkSize(int width, int height, int depth) {
+        globalWorkSize.set(3, width, height, depth);
+    }
+
+    /**
+     * @return the current work group size
+     */
+    public WorkSize getWorkGroupSize() {
+        return workGroupSize;
+    }
+
+    /**
+     * Sets the work group size
+     * @param ws the work group size to set
+     */
+    public void setWorkGroupSize(WorkSize ws) {
+        workGroupSize.set(ws);
+    }
+
+    /**
+     * Sets the work group size to be a 1D grid
+     * @param size the size to set
+     */
+    public void setWorkGroupSize(int size) {
+        workGroupSize.set(1, size);
+    }
+
+    /**
+     * Sets the work group size to be a 2D grid
+     * @param width the width
+     * @param height the height
+     */
+    public void setWorkGroupSize(int width, int height) {
+        workGroupSize.set(2, width, height);
+    }
+
+    /**
+     * Sets the work group size to be a 3D grid
+     * @param width the width
+     * @param height the height
+     * @param depth the depth
+     */
+    public void setWorkGroupSdize(int width, int height, int depth) {
+        workGroupSize.set(3, width, height, depth);
+    }
+    
+    /**
+     * Tells the driver to figure out the work group size on their own.
+     * Use this if you do not rely on specific work group layouts, i.e.
+     * because shared memory is not used.
+     * {@link #Run1(com.jme3.opencl.CommandQueue, com.jme3.opencl.Kernel.WorkSize, java.lang.Object...) }
+     * implicetly calls this mehtod.
+     */
+    public void setWorkGroupSizeToNull() {
+        workGroupSize.set(1, 0, 0, 0);
+    }
+    
+    /**
+     * Returns the maximal work group size when this kernel is executed on
+     * the specified device
+     * @param device the device
+     * @return the maximal work group size
+     */
+    public abstract long getMaxWorkGroupSize(Device device);
+
+    public abstract void setArg(int index, LocalMemPerElement t);
+
+    public abstract void setArg(int index, LocalMem t);
+
+    public abstract void setArg(int index, Buffer t);
+    
+    public abstract void setArg(int index, Image i);
+
+    public abstract void setArg(int index, byte b);
+
+    public abstract void setArg(int index, short s);
+
+    public abstract void setArg(int index, int i);
+
+    public abstract void setArg(int index, long l);
+
+    public abstract void setArg(int index, float f);
+
+    public abstract void setArg(int index, double d);
+
+    public abstract void setArg(int index, Vector2f v);
+  
+    public abstract void setArg(int index, Vector4f v);
+
+    public abstract void setArg(int index, Quaternion q);
+    
+    public abstract void setArg(int index, Matrix4f mat);
+    
+    public void setArg(int index, Matrix3f mat) {
+        TempVars vars = TempVars.get();
+        try {
+            Matrix4f m = vars.tempMat4;
+            m.zero();
+            for (int i=0; i<3; ++i) {
+                for (int j=0; j<3; ++j) {
+                    m.set(i, j, mat.get(i, j));
+                }
+            }
+            setArg(index, m);
+        } finally {
+            vars.release();
+        }
+    }
+    
+    /**
+     * Raw version to set an argument.
+     * {@code size} bytes of the provided byte buffer are copied to the kernel
+     * argument. The size in bytes must match exactly the argument size
+     * as defined in the kernel code.
+     * Use this method to send custom structures to the kernel
+     * @param index the index of the argument
+     * @param buffer the raw buffer
+     * @param size the size in bytes
+     */
+    public abstract void setArg(int index, ByteBuffer buffer, long size);
+
+    /**
+     * Sets the kernel argument at the specified index.<br>
+     * The argument must be a known type:
+     * {@code LocalMemPerElement, LocalMem, Image, Buffer, byte, short, int,
+     * long, float, double, Vector2f, Vector4f, Quaternion, Matrix3f, Matrix4f}.
+     * <br>
+     * Note: Matrix3f and Matrix4f will be mapped to a {@code float16} (row major).
+     * @param index the index of the argument, from 0 to {@link #getArgCount()}-1
+     * @param arg the argument
+     * @throws IllegalArgumentException if the argument type is not one of the listed ones
+     */
+    public void setArg(int index, Object arg) {
+        if (arg instanceof Byte) {
+            setArg(index, (byte) arg);
+        } else if (arg instanceof Short) {
+            setArg(index, (short) arg);
+        } else if (arg instanceof Integer) {
+            setArg(index, (int) arg);
+        } else if (arg instanceof Long) {
+            setArg(index, (long) arg);
+        } else if (arg instanceof Float) {
+            setArg(index, (float) arg);
+        } else if (arg instanceof Double) {
+            setArg(index, (double) arg);
+        } else if (arg instanceof Vector2f) {
+            setArg(index, (Vector2f) arg);
+        } else if (arg instanceof Vector4f) {
+            setArg(index, (Vector4f) arg);
+        } else if (arg instanceof Quaternion) {
+            setArg(index, (Quaternion) arg);
+        } else if (arg instanceof Matrix3f) {
+            setArg(index, (Matrix3f) arg);
+        } else if (arg instanceof Matrix4f) {
+            setArg(index, (Matrix4f) arg);
+        } else if (arg instanceof LocalMemPerElement) {
+            setArg(index, (LocalMemPerElement) arg);
+        } else if (arg instanceof LocalMem) {
+            setArg(index, (LocalMem) arg);
+        } else if (arg instanceof Buffer) {
+            setArg(index, (Buffer) arg);
+        } else if (arg instanceof Image) {
+            setArg(index, (Image) arg);
+        } else {
+            throw new IllegalArgumentException("unknown kernel argument type: " + arg);
+        }
+    }
+
+    private void setArgs(Object... args) {
+        for (int i = 0; i < args.length; ++i) {
+            setArg(i, args[i]);
+        }
+    }
+
+    /**
+     * Launches the kernel with the current global work size, work group size
+     * and arguments.
+     * If the returned event object is not needed and would otherwise be
+     * released immediately, {@link #RunNoEvent(com.jme3.opencl.CommandQueue) }
+     * might bring a better performance.
+     * @param queue the command queue
+     * @return an event object indicating when the kernel is finished
+     * @see #setGlobalWorkSize(com.jme3.opencl.Kernel.WorkSize) 
+     * @see #setWorkGroupSize(com.jme3.opencl.Kernel.WorkSize) 
+     * @see #setArg(int, java.lang.Object) 
+     */
+    public abstract Event Run(CommandQueue queue);
+    
+    /**
+     * Launches the kernel with the current global work size, work group size
+     * and arguments without returning an event object.
+     * The generated event is directly released. Therefore, the performance
+     * is better, but there is no way to detect when the kernel execution
+     * has finished. For this purpose, use {@link #Run(com.jme3.opencl.CommandQueue) }.
+     * @param queue the command queue
+     * @see #setGlobalWorkSize(com.jme3.opencl.Kernel.WorkSize) 
+     * @see #setWorkGroupSize(com.jme3.opencl.Kernel.WorkSize) 
+     * @see #setArg(int, java.lang.Object) 
+     */
+    public void RunNoEvent(CommandQueue queue) {
+        //Default implementation, overwrite to not allocate the event object
+        Run(queue).release();
+    }
+
+    /**
+     * Sets the work sizes and arguments in one call and launches the kernel.
+     * The global work size is set to the specified size. The work group
+     * size is automatically determined by the driver.
+     * Each object in the argument array is sent to the kernel by
+     * {@link #setArg(int, java.lang.Object) }.
+     * @param queue the command queue
+     * @param globalWorkSize the global work size
+     * @param args the kernel arguments
+     * @return an event object indicating when the kernel is finished
+     * @see #Run2(com.jme3.opencl.CommandQueue, com.jme3.opencl.Kernel.WorkSize, com.jme3.opencl.Kernel.WorkSize, java.lang.Object...) 
+     */
+    public Event Run1(CommandQueue queue, WorkSize globalWorkSize, Object... args) {
+        setGlobalWorkSize(globalWorkSize);
+        setWorkGroupSizeToNull();
+        setArgs(args);
+        return Run(queue);
+    }
+    
+    /**
+     * Sets the work sizes and arguments in one call and launches the kernel.
+     * The global work size is set to the specified size. The work group
+     * size is automatically determined by the driver.
+     * Each object in the argument array is sent to the kernel by
+     * {@link #setArg(int, java.lang.Object) }.
+     * The generated event is directly released. Therefore, the performance
+     * is better, but there is no way to detect when the kernel execution
+     * has finished. For this purpose, use 
+     * {@link #Run1(com.jme3.opencl.CommandQueue, com.jme3.opencl.Kernel.WorkSize, java.lang.Object...) }.
+     * @param queue the command queue
+     * @param globalWorkSize the global work size
+     * @param args the kernel arguments
+     * @see #Run2(com.jme3.opencl.CommandQueue, com.jme3.opencl.Kernel.WorkSize, com.jme3.opencl.Kernel.WorkSize, java.lang.Object...) 
+     */
+    public void Run1NoEvent(CommandQueue queue, WorkSize globalWorkSize, Object... args) {
+        setGlobalWorkSize(globalWorkSize);
+        setWorkGroupSizeToNull();
+        setArgs(args);
+        RunNoEvent(queue);
+    }
+
+    /**
+     * Sets the work sizes and arguments in one call and launches the kernel.
+     * @param queue the command queue
+     * @param globalWorkSize the global work size
+     * @param workGroupSize the work group size
+     * @param args the kernel arguments
+     * @return an event object indicating when the kernel is finished
+     */
+    public Event Run2(CommandQueue queue, WorkSize globalWorkSize,
+            WorkSize workGroupSize, Object... args) {
+        setGlobalWorkSize(globalWorkSize);
+        setWorkGroupSize(workGroupSize);
+        setArgs(args);
+        return Run(queue);
+    }
+
+    /**
+     * Sets the work sizes and arguments in one call and launches the kernel.
+     * The generated event is directly released. Therefore, the performance
+     * is better, but there is no way to detect when the kernel execution
+     * has finished. For this purpose, use 
+     * {@link #Run2(com.jme3.opencl.CommandQueue, com.jme3.opencl.Kernel.WorkSize, com.jme3.opencl.Kernel.WorkSize, java.lang.Object...) }.
+     * @param queue the command queue
+     * @param globalWorkSize the global work size
+     * @param workGroupSize the work group size
+     * @param args the kernel arguments
+     */
+    public void Run2NoEvent(CommandQueue queue, WorkSize globalWorkSize,
+            WorkSize workGroupSize, Object... args) {
+        setGlobalWorkSize(globalWorkSize);
+        setWorkGroupSize(workGroupSize);
+        setArgs(args);
+        RunNoEvent(queue);
+    }
+    
+    /**
+     * A placeholder for kernel arguments representing local kernel memory.
+     * This defines the size of available shared memory of a {@code __shared} kernel
+     * argument
+     */
+    public static final class LocalMem {
+
+        private int size;
+
+        /**
+         * Creates a new LocalMem instance
+         * @param size the size of the available shared memory in bytes
+         */
+        public LocalMem(int size) {
+            super();
+            this.size = size;
+        }
+
+        public int getSize() {
+            return size;
+        }
+
+        @Override
+        public int hashCode() {
+            int hash = 3;
+            hash = 79 * hash + this.size;
+            return hash;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == null) {
+                return false;
+            }
+            if (getClass() != obj.getClass()) {
+                return false;
+            }
+            final LocalMem other = (LocalMem) obj;
+            if (this.size != other.size) {
+                return false;
+            }
+            return true;
+        }
+    }
+
+    /**
+     * A placeholder for a kernel argument representing local kernel memory per thread.
+     * This effectively computes {@code SharedMemoryPerElement * WorkGroupSize}
+     * and uses this value as the size of shared memory available in the kernel.
+     * Therefore, an instance of this class must be set as an argument AFTER
+     * the work group size has been specified. This is
+     * ensured by {@link #Run2(com.jme3.opencl.CommandQueue, com.jme3.opencl.Kernel.WorkSize, com.jme3.opencl.Kernel.WorkSize, java.lang.Object...) }.
+     * This argument can't be used when no work group size was defined explicetly
+     * (e.g. by {@link #setWorkGroupSizeToNull()} or {@link #Run1(com.jme3.opencl.CommandQueue, com.jme3.opencl.Kernel.WorkSize, java.lang.Object...) }.
+     */
+    public static final class LocalMemPerElement {
+
+        private int size;
+
+        /**
+         * Creates a new LocalMemPerElement instance
+         * @param size the number of bytes available for each thread within
+         * a work group
+         */
+        public LocalMemPerElement(int size) {
+            super();
+            this.size = size;
+        }
+
+        public int getSize() {
+            return size;
+        }
+
+        @Override
+        public int hashCode() {
+            int hash = 3;
+            hash = 79 * hash + this.size;
+            return hash;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == null) {
+                return false;
+            }
+            if (getClass() != obj.getClass()) {
+                return false;
+            }
+            final LocalMemPerElement other = (LocalMemPerElement) obj;
+            if (this.size != other.size) {
+                return false;
+            }
+            return true;
+        }
+    }
+
+    /**
+     * The work size (global and local) for executing a kernel
+     * @author shaman
+     */
+    public static final class WorkSize {
+
+        private int dimension;
+        private long[] sizes;
+
+        /**
+         * Creates a new work size object
+         * @param dimension the dimension (1,2,3)
+         * @param sizes the sizes in each dimension, the length must match the specified dimension
+         */
+        public WorkSize(int dimension, long... sizes) {
+            super();
+            set(dimension, sizes);
+        }
+
+        /**
+         * Creates a work size of dimension 1 and extend 1,1,1 (only one thread).
+         */
+        public WorkSize() {
+            this(1, 1, 1, 1);
+        }
+
+        /**
+         * Creates a 1D work size of the specified extend
+         * @param size the size
+         */
+        public WorkSize(long size) {
+            this(1, size, 1, 1);
+        }
+
+        /**
+         * Creates a 2D work size of the specified extend
+         * @param width the width
+         * @param height the height
+         */
+        public WorkSize(long width, long height) {
+            this(2, width, height, 1);
+        }
+
+        /**
+         * Creates a 3D work size of the specified extend.
+         * @param width the width
+         * @param height the height
+         * @param depth the depth
+         */
+        public WorkSize(long width, long height, long depth) {
+            this(3, width, height, depth);
+        }
+
+        public int getDimension() {
+            return dimension;
+        }
+
+        public long[] getSizes() {
+            return sizes;
+        }
+
+        public void set(int dimension, long... sizes) {
+            if (sizes == null || sizes.length != 3) {
+                throw new IllegalArgumentException("sizes must be an array of length 3");
+            }
+            if (dimension <= 0 || dimension > 3) {
+                throw new IllegalArgumentException("dimension must be between 1 and 3");
+            }
+            this.dimension = dimension;
+            this.sizes = sizes;
+        }
+
+        public void set(WorkSize ws) {
+            this.dimension = ws.dimension;
+            this.sizes = ws.sizes;
+        }
+
+        @Override
+        public int hashCode() {
+            int hash = 5;
+            hash = 47 * hash + this.dimension;
+            hash = 47 * hash + Arrays.hashCode(this.sizes);
+            return hash;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == null) {
+                return false;
+            }
+            if (getClass() != obj.getClass()) {
+                return false;
+            }
+            final WorkSize other = (WorkSize) obj;
+            if (this.dimension != other.dimension) {
+                return false;
+            }
+            if (!Arrays.equals(this.sizes, other.sizes)) {
+                return false;
+            }
+            return true;
+        }
+    }
+    
+}

+ 58 - 0
jme3-core/src/main/java/com/jme3/opencl/KernelCompilationException.java

@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl;
+
+/**
+ * This exception is thrown by {@link Program#build() } and {@link Program#build(java.lang.String) }
+ * when the compilation failed.
+ * The error log returned by {@link #getLog() } contains detailed information
+ * where the error occured.
+ * @author shaman
+ */
+public class KernelCompilationException extends OpenCLException {
+
+	private final String log;
+	
+	public KernelCompilationException(String msg, int errorCode, String log) {
+		super(msg, errorCode);
+		this.log = log;
+	}
+
+    /**
+     * The output of the compiler
+     * @return 
+     */
+	public String getLog() {
+		return log;
+	}
+
+}

+ 58 - 0
jme3-core/src/main/java/com/jme3/opencl/MappingAccess.java

@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl;
+
+/**
+ * Specifies the access flags when mapping a {@link Buffer} or {@link Image} object.
+ * @see Buffer#map(com.jme3.opencl.CommandQueue, long, long, com.jme3.opencl.MappingAccess) 
+ * @see Image#map(com.jme3.opencl.CommandQueue, long[], long[], com.jme3.opencl.MappingAccess) 
+ * @author shaman
+ */
+public enum MappingAccess {
+    /**
+     * Only read access is allowed to the mapped memory.
+     */
+	MAP_READ_ONLY,
+    /**
+     * Only write access is allowed to the mapped memory.
+     */
+	MAP_WRITE_ONLY,
+    /**
+     * Both read and write access is allowed.
+     */
+	MAP_READ_WRITE,
+    /**
+     * The old memory content is completely discarded and the buffer is filled
+     * completely with new data. This might be faster than {@link #MAP_WRITE_ONLY}
+     */
+	MAP_WRITE_INVALIDATE
+}

+ 52 - 0
jme3-core/src/main/java/com/jme3/opencl/MemoryAccess.java

@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl;
+
+/**
+ * Specifies how a buffer object can be accessed by the kernel.
+ * @author shaman
+ * @see Buffer
+ */
+public enum MemoryAccess {
+    /**
+     * A kernel can both read and write the buffer.
+     */
+	READ_WRITE,
+    /**
+     * A kernel can only write this buffer.
+     */
+	WRITE_ONLY,
+    /**
+     * A kernel can only read this buffer
+     */
+	READ_ONLY
+}

+ 78 - 0
jme3-core/src/main/java/com/jme3/opencl/OpenCLException.java

@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl;
+
+/**
+ * Generic OpenCL exception, can be thrown in every method of this package.
+ * The error code and its name is reported in the message string as well as the OpenCL call that
+ * causes this exception. Please refer to the official OpenCL specification
+ * to see what might cause this exception.
+ * @author shaman
+ */
+public class OpenCLException extends RuntimeException {
+    private static final long serialVersionUID = 8471229972153694848L;
+
+	private final int errorCode;
+	
+	/**
+	 * Creates a new instance of <code>OpenCLExceptionn</code> without detail
+	 * message.
+	 */
+	public OpenCLException() {
+		errorCode = 0;
+	}
+
+	/**
+	 * Constructs an instance of <code>OpenCLExceptionn</code> with the
+	 * specified detail message.
+	 *
+	 * @param msg the detail message.
+	 */
+	public OpenCLException(String msg) {
+		super(msg);
+		errorCode = 0;
+	}
+	
+	public OpenCLException(String msg, int errorCode) {
+		super(msg);
+		this.errorCode = errorCode;
+	}
+
+    /**
+     * @return the error code
+     */
+	public int getErrorCode() {
+		return errorCode;
+	}
+
+	
+}

+ 75 - 0
jme3-core/src/main/java/com/jme3/opencl/OpenCLObject.java

@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl;
+
+/**
+ * Base interface of all native OpenCL objects.
+ * This interface provides the functionality for savely release the object.
+ * @author shaman
+ */
+public interface OpenCLObject {
+    
+    /**
+     * Releaser for an {@link OpenCLObject}.
+     * Implementations of this interface must not hold a reference to the
+     * {@code OpenCLObject} directly.
+     */
+    public static interface ObjectReleaser {
+        /**
+         * Releases the native resources of the associated {@link OpenCLObject}.
+         * This method must be guarded against multiple calls: only the first
+         * call should release, the next ones must not throw an exception.
+         */
+        void release();
+    }
+    /**
+     * Returns the releaser object. Multiple calls should return the same object.
+     * The ObjectReleaser is used to release the OpenCLObject when it is garbage
+     * collected. Therefore, the returned object must not hold a reference to
+     * the OpenCLObject.
+     * @return the object releaser
+     */
+    ObjectReleaser getReleaser();
+    /**
+     * Releases this native object.
+     * Should delegate to {@code getReleaser().release()}.
+     */
+    void release();
+    /**
+     * Registers this object for automatic releasing on garbage collection.
+     * By default, OpenCLObjects are not registered in the
+     * {@link OpenCLObjectManager}, you have to release it manually 
+     * by calling {@link #release() }.
+     * Without registering or releasing, a memory leak might occur.
+     */
+    void register();
+}

+ 123 - 0
jme3-core/src/main/java/com/jme3/opencl/OpenCLObjectManager.java

@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl;
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.ReferenceQueue;
+import java.util.HashSet;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author shaman
+ */
+public class OpenCLObjectManager {
+    private static final Logger LOG = Logger.getLogger(OpenCLObjectManager.class.getName());
+    private static final Level LOG_LEVEL1 = Level.FINER;
+    private static final Level LOG_LEVEL2 = Level.FINE;
+    /**
+     * Call Runtime.getRuntime().gc() every these frames
+     */
+    private static final int GC_FREQUENCY = 10;
+    
+    private static final OpenCLObjectManager INSTANCE = new OpenCLObjectManager();
+    private OpenCLObjectManager() {}
+    
+    public static OpenCLObjectManager getInstance() {
+        return INSTANCE;
+    }
+    
+    private ReferenceQueue<Object> refQueue = new ReferenceQueue<Object>();
+    private HashSet<OpenCLObjectRef> activeObjects = new HashSet<OpenCLObjectRef>();
+    private int gcCounter = 0;
+    
+    private static class OpenCLObjectRef extends PhantomReference<Object> {
+        
+        private OpenCLObject.ObjectReleaser releaser;
+
+        public OpenCLObjectRef(ReferenceQueue<Object> refQueue, OpenCLObject obj){
+            super(obj, refQueue);
+            releaser = obj.getReleaser();
+        }
+    }
+    
+    public void registerObject(OpenCLObject obj) {
+        OpenCLObjectRef ref = new OpenCLObjectRef(refQueue, obj);
+        activeObjects.add(ref);
+        LOG.log(LOG_LEVEL1, "registered OpenCL object: {0}", obj);
+    }
+    
+    private void deleteObject(OpenCLObjectRef ref) {
+        LOG.log(LOG_LEVEL1, "deleting OpenCL object by: {0}", ref.releaser);
+        ref.releaser.release();
+        activeObjects.remove(ref);
+    }
+        
+    public void deleteUnusedObjects() {
+        if (activeObjects.isEmpty()) {
+            LOG.log(LOG_LEVEL2, "no active natives");
+            return; //nothing to do
+        }
+        
+        gcCounter++;
+        if (gcCounter >= GC_FREQUENCY) {
+            //The program is that the OpenCLObjects are so small that they are 
+            //enqueued for finalization very late. Therefore, without this
+            //hack, we are running out of host memory on the OpenCL side quickly.
+            gcCounter = 0;
+            Runtime.getRuntime().gc();
+        }
+        
+        int removed = 0;
+        while (true) {
+            // Remove objects reclaimed by GC.
+            OpenCLObjectRef ref = (OpenCLObjectRef) refQueue.poll();
+            if (ref == null) {
+                break;
+            }
+            deleteObject(ref);
+            removed++;
+        }
+        if (removed >= 1) {
+            LOG.log(LOG_LEVEL2, "{0} native objects were removed from native", removed);
+        }
+    }
+    
+    public void deleteAllObjects() {
+        for (OpenCLObjectRef ref : activeObjects) {
+            LOG.log(LOG_LEVEL1, "deleting OpenCL object by: {0}", ref.releaser);
+            ref.releaser.release();
+        }
+        activeObjects.clear();
+    }
+}

+ 107 - 0
jme3-core/src/main/java/com/jme3/opencl/Platform.java

@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * A wrapper for an OpenCL platform. A platform is the highest object in the
+ * object hierarchy, it creates the {@link Device}s which are then used to
+ * create the {@link Context}.<br>
+ * This class is mostly used within {@link PlatformChooser}.
+ * 
+ * @author shaman
+ */
+public interface Platform {
+
+    /**
+     * @return the list of available devices for this platform
+     */
+    List<? extends Device> getDevices();
+    
+    /**
+     * @return The profile string
+     */
+    String getProfile();
+    /**
+     * @return {@code true} if this platform implements the full profile
+     */
+    boolean isFullProfile();
+    /**
+     * @return {@code true} if this platform implements the embedded profile
+     */
+    boolean isEmbeddedProfile();
+    
+    /**
+     * @return the version string
+     */
+    String getVersion();
+    /**
+     * Extracts the major version from the version string
+     * @return the major version
+     */
+    int getVersionMajor();
+    /**
+     * Extracts the minor version from the version string
+     * @return the minor version
+     */
+    int getVersionMinor();
+    
+    /**
+     * @return the name of the platform
+     */
+    String getName();
+    /**
+     * @return the vendor of the platform
+     */
+    String getVendor();
+    /**
+     * Queries if this platform supports OpenGL interop at all.
+     * This value has also to be tested for every device.
+     * @return {@code true} if OpenGL interop is supported
+     */
+    boolean hasOpenGLInterop();
+    /**
+     * Queries if the specified extension is available.
+     * This value has to be tested also for every device.
+     * @param extension the extension string
+     * @return {@code true} if this extension is supported by the platform
+     * (however, not all devices might support it as well)
+     */
+    boolean hasExtension(String extension);
+    /**
+     * @return All available extensions
+     */
+    Collection<? extends String> getExtensions();
+    
+}

+ 54 - 0
jme3-core/src/main/java/com/jme3/opencl/PlatformChooser.java

@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl;
+
+import com.jme3.system.AppSettings;
+import java.util.List;
+
+/**
+ * This SPI is called on startup to specify which platform and which devices
+ * are used for context creation.
+ * @author shaman
+ * @see AppSettings#setOpenCLPlatformChooser(java.lang.Class) 
+ */
+public interface PlatformChooser {
+    
+    /**
+     * Chooses one or more devices for the opencl context.
+     * All returned devices must belong to the same platform.
+     * If the returned list is empty, no context will be created.
+     * @param platforms the available platforms
+     * @return the list of devices
+     */
+    List<? extends Device> chooseDevices(List<? extends Platform> platforms);
+    
+}

+ 104 - 0
jme3-core/src/main/java/com/jme3/opencl/Program.java

@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl;
+
+import java.nio.ByteBuffer;
+
+/**
+ * A wrapper for an OpenCL program. A program is created from kernel source code,
+ * manages the build process and creates the kernels.
+ * <p>
+ * Warning: Creating the same kernel more than one leads to undefined behaviour,
+ * this is especially important for {@link #createAllKernels() }
+ * 
+ * @see Context#createProgramFromSourceCode(java.lang.String) 
+ * @see #createKernel(java.lang.String) 
+ * @author shaman
+ */
+public abstract class Program extends AbstractOpenCLObject {
+
+    protected Program(ObjectReleaser releaser) {
+        super(releaser);
+    }
+    
+    /**
+     * Builds this program with the specified argument string on the specified
+     * devices.
+     * Please see the official OpenCL specification for a definition of
+     * all supported arguments.
+     * The list of devices specify on which device the compiled program
+     * can then be executed. It must be a subset of {@link Context#getDevices() }.
+     * If {@code null} is passed, the program is built on all available devices.
+     * 
+     * @param args the compilation arguments
+     * @param devices a list of devices on which the program is build.
+     * @throws KernelCompilationException if the compilation fails
+     * @see #build() 
+     */
+	public abstract void build(String args, Device... devices) throws KernelCompilationException;
+    /**
+     * Builds this program without additional arguments
+     * @throws KernelCompilationException if the compilation fails
+     * @see #build(java.lang.String) 
+     */
+	public void build() throws KernelCompilationException {
+        build("", (Device[]) null);
+    }
+
+    /**
+     * Creates the kernel with the specified name.
+     * @param name the name of the kernel as defined in the source code
+     * @return the kernel object
+     * @throws OpenCLException if the kernel was not found or some other
+     * error occured
+     */
+	public abstract Kernel createKernel(String name);
+    
+    /**
+     * Creates all available kernels in this program.
+     * The names of the kernels can then by queried by {@link Kernel#getName() }.
+     * @return an array of all kernels
+     */
+	public abstract Kernel[] createAllKernels();
+    
+    /**
+     * Queries a compiled binary representation of this program for a particular
+     * device. This binary can then be used e.g. in the next application launch
+     * to create the program from the binaries and not from the sources.
+     * This saves time.
+     * @param device the device from which the binaries are taken
+     * @return the binaries
+     * @see Context#createProgramFromBinary(java.nio.ByteBuffer, com.jme3.opencl.Device) 
+     */
+    public abstract ByteBuffer getBinary(Device device);
+    
+}

+ 229 - 0
jme3-core/src/main/java/com/jme3/opencl/ProgramCache.java

@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl;
+
+import com.jme3.system.JmeSystem;
+import com.jme3.util.BufferUtils;
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Implements a simple cache system for program objects.
+ * The program objects are saved persistently with {@link #saveToCache(java.lang.String, com.jme3.opencl.Program) }.
+ * On the next run, the stored programs can then be loaded
+ * with {@link #loadFromCache(java.lang.String, java.lang.String) }.
+ * <br>
+ * The programs are identified by a unique id. The following format is recommended:
+ * {@code id = <full name of the class using the program>.<unique identifier within that class>}.
+ * 
+ * @author shaman
+ */
+public class ProgramCache {
+    private static final Logger LOG = Logger.getLogger(ProgramCache.class.getName());
+    private static final String FILE_EXTENSION = ".clbin";
+    
+    private final Context context;
+    private final Device device;
+    private final File tmpFolder;
+
+    /**
+     * Creates a "disabled" program cache, no caching is done.
+     * {@link #loadFromCache(java.lang.String) } will always return {@code null}
+     * and {@link #saveToCache(java.lang.String, com.jme3.opencl.Program) } does
+     * nothing.<br>
+     * Use this during development if you still modify your kernel code.
+     * (Otherwise, you don't see the changes because you are still use the 
+     * cached version of your program)
+     */
+    public ProgramCache() {
+        this.context = null;
+        this.device = null;
+        this.tmpFolder = null;
+    }
+    
+    /**
+     * Creates a new program cache associated with the specified context and
+     * devices.
+     * The cached programs are built against the specified device and also
+     * only the binaries linked to that device are stored.
+     * @param context the OpenCL context
+     * @param device the OpenCL device
+     */
+    public ProgramCache(Context context, Device device) {
+        this.context = context;
+        this.device = device;
+        if (JmeSystem.isLowPermissions()) {
+            tmpFolder = null;
+        } else {
+            tmpFolder = JmeSystem.getStorageFolder();
+        }
+    }
+    
+    protected String getCleanFileName(String id) {
+        //http://stackoverflow.com/a/35591188/4053176
+        return id.replaceAll("[^a-zA-Z0-9.-]", "") + FILE_EXTENSION;
+    }
+
+    /**
+     * Creates a new program cache using the first device from the specified 
+     * context.
+     * @param context the context
+     * @see #ProgramCache(com.jme3.opencl.Context, com.jme3.opencl.Device) 
+     */
+    public ProgramCache(Context context) {
+        this(context, context.getDevices().get(0));
+    }
+
+    /**
+     * Loads the program from the cache and builds it against the current device.
+     * You can pass additional build arguments with the parameter {@code buildArgs}.
+     * <p>
+     * The cached program is identified by the specified id. 
+     * This id must be unique, otherwise collisions within the cache occur.
+     * Therefore, the following naming schema is recommended:
+     * {@code id = <full name of the class using the program>.<unique identifier within that class>}.
+     * <p>
+     * If the program can't be loaded, built or any other exception happened,
+     * {@code null} is returned.
+     * 
+     * @param id the unique identifier of this program
+     * @param buildArgs additional build arguments, can be {@code null}
+     * @return the loaded and built program, or {@code null}
+     * @see #saveToCache(java.lang.String, com.jme3.opencl.Program) 
+     */
+    public Program loadFromCache(String id, String buildArgs) {
+        if (tmpFolder == null) {
+            return null; //low permissions
+        }
+        //get file
+        File file = new File(tmpFolder, getCleanFileName(id));
+        if (!file.exists()) {
+            if (LOG.isLoggable(Level.FINE)) {
+                LOG.log(Level.FINE, "Cache file {0} does not exist", file.getAbsolutePath());
+            }
+            return null;
+        }
+        //load from file
+        ByteBuffer bb;
+        try {
+            byte[] bytes = Files.readAllBytes(file.toPath());
+            bb = BufferUtils.createByteBuffer(bytes);
+        } catch (IOException ex) {
+            LOG.log(Level.FINE, "Unable to read cache file", ex);
+            return null;
+        }
+        //create program
+        Program program;
+        try {
+            program = context.createProgramFromBinary(bb, device);
+        } catch (OpenCLException ex) {
+            LOG.log(Level.FINE, "Unable to create program from binary", ex);
+            return null;
+        }
+        //build program
+        try {
+            program.build(buildArgs, device);
+        } catch (OpenCLException ex) {
+            LOG.log(Level.FINE, "Unable to build program", ex);
+            return null;
+        }
+        //done
+        return program;
+    }
+    
+    /**
+     * Calls {@link #loadFromCache(java.lang.String, java.lang.String) }
+     * with the additional build arguments set to {@code ""}.
+     * @param id a unique identifier of the program
+     * @return the loaded and built program or {@code null} if this
+     * program could not be loaded from the cache
+     * @see #loadFromCache(java.lang.String, java.lang.String) 
+     */
+    public Program loadFromCache(String id) {
+        return loadFromCache(id, "");
+    }
+    
+    /**
+     * Saves the specified program in the cache.
+     * The parameter {@code id} denotes the name of the program. Under this id,
+     * the program is then loaded again by {@link #loadFromCache(java.lang.String, java.lang.String) }.
+     * <br>
+     * The id must be unique, otherwise collisions within the cache occur.
+     * Therefore, the following naming schema is recommended:
+     * {@code id = <full name of the class using the program>.<unique identifier within that class>}.
+     * 
+     * @param id the program id
+     * @param program the program to store in the cache
+     */
+    public void saveToCache(String id, Program program) {
+        if (tmpFolder == null) {
+            return; //low permissions
+        }
+        //get file
+        File file = new File(tmpFolder, getCleanFileName(id));
+        //get binaries
+        ByteBuffer bb;
+        try {
+            bb = program.getBinary(device);
+        } catch (UnsupportedOperationException | OpenCLException ex) {
+            LOG.log(Level.WARNING, "Unable to retrieve the program binaries", ex);
+            return;
+        }
+        byte[] bytes = new byte[bb.remaining()];
+        bb.get(bytes);
+        //save
+        try {
+            Files.write(file.toPath(), bytes);
+        } catch (IOException ex) {
+           LOG.log(Level.WARNING, "Unable to save program binaries to the cache", ex);
+        }
+    }
+    
+    /**
+     * Clears the cache.
+     * All saved program binaries are deleted.
+     */
+    public void clearCache() {
+        if (tmpFolder == null) {
+            return; //low permissions
+        }
+        for (File file : tmpFolder.listFiles()) {
+            if (file.isFile() && file.getName().endsWith(FILE_EXTENSION)) {
+                file.delete();
+            }
+        }
+    }
+}

+ 163 - 0
jme3-core/src/main/java/com/jme3/opencl/package-info.java

@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * This package contains an API for using OpenCL together with jME3.
+ * <p>
+ * <b>Activation:</b><br>
+ * OpenCL is deactivated by default. To activate it, set {@link com.jme3.system.AppSettings#setOpenCLSupport(boolean) }
+ * to {@code true}.
+ * If the current platform supports OpenCL, then the central {@link com.jme3.opencl.Context} 
+ * can be fetched by {@link com.jme3.system.JmeContext#getOpenCLContext() } which is
+ * available in each application. If OpenCL is deactivated or not available, 
+ * this method returns {@code null}.
+ * 
+ * <p>
+ * <b>First steps:</b><br>
+ * Once you have obtained your {@link com.jme3.opencl.Context} you start by
+ * creating a {@link com.jme3.opencl.CommandQueue} by calling 
+ * {@link com.jme3.opencl.Context#createQueue() } or alternative versions.
+ * The command queue must be passed to every following method that execute
+ * some action involving the GPU. All actions are executed in the order in which they
+ * are added to the queue.
+ * <br>
+ * <b>Programs and Kernels:</b>
+ * The main purpose of OpenCL is to execute code in parallel
+ * on the GPU. From the source code, a {@link com.jme3.opencl.Program} object
+ * is created by {@link com.jme3.opencl.Context#createProgramFromSourceCode(java.lang.String) },
+ * {@link com.jme3.opencl.Context#createProgramFromSourceFilesWithInclude(com.jme3.asset.AssetManager, java.lang.String, java.util.List) }
+ * or alternative versions.
+ * Before using it, the source code must be build using {@link com.jme3.opencl.Program#build() }.
+ * Any compilation error is thrown here. Each program consists of multiple kernels.
+ * Each kernel represents one executable unit and is declared in the source code
+ * with the following syntax: {@code __kernel void KernelName(KernelArgs) {Code} }.
+ * On the programming side, a {@link com.jme3.opencl.Kernel} instance is obtained
+ * by calling {@link com.jme3.opencl.Program#createKernel(java.lang.String) }.
+ * To execute the kernel, the method {@link com.jme3.opencl.Kernel#Run1(com.jme3.opencl.CommandQueue, com.jme3.opencl.WorkSize, java.lang.Object...) }
+ * is provided. You first pass the command queue and the work size (i.e. the number of parallel executed threads)
+ * followed by the kernel arguments.
+ * <br>
+ * <b>Buffers and Images:</b>
+ * OpenCL Kernels show their true power first when they operate on buffers and images.
+ * Buffers are simple one dimensional consecutive chunks of memory of arbitrary size.
+ * These {@link com.jme3.opencl.Buffer} instances are created by calling
+ * {@link com.jme3.opencl.Context#createBuffer(long)} with the size in bytes as
+ * the argument. A buffer on its own is typeless. In the kernel, you then specify
+ * the type of the buffer by argument declarations like {@code __global float* buffer}.
+ * Note that OpenCL does not check buffer boundaries. If you read or write outside
+ * of the buffer, the behavior is completely undefined and may often result in
+ * a program cache later on.
+ * {@link com.jme3.opencl.Image} objects are structured one, two or three dimensional
+ * memory chunks of a fixed type. They are created by 
+ * {@link com.jme3.opencl.Context#createImage(com.jme3.opencl.MemoryAccess, com.jme3.opencl.Image.ImageFormat, com.jme3.opencl.Image.ImageDescriptor, java.nio.ByteBuffer) }.
+ * They need special functions in the kernel code to write to or read from images.
+ * Both buffer and image objects provide methods for copying between buffers and images,
+ * reading and writing to host code and directly mapping memory parts to the host code.
+ * <br>
+ * <b>Events:</b>
+ * Most methods are provided in two variations: blocking calls or asynchronous
+ * calls (the later one have the suffix -Async, or all kernel calls).
+ * These async calls all return {@link com.jme3.opencl.Event} objects.
+ * These events can be used to check (non-blocking) if the action has completed, e.g. a memory copy
+ * is finished, or to block the execution until the action has finished.
+ * <br>
+ * Some methods have the suffix {@code -NoEvent}. This means that these methods
+ * don't return an event object even if the OpenCL function would return an event.
+ * There exists always an alternative version that does return an event.
+ * These methods exist to increase the performance: since all actions (like multiple kernel calls)
+ * that are sent to the same command queue are executed in order, there is no
+ * need for intermediate events. (These intermediate events would be released
+ * immediately). Therefore, the no-event alternatives increase the performance
+ * because no additional event object has to be allocated and less system calls
+ * are neccessary.
+ * 
+ * <p>
+ * <b>Interoperability between OpenCL and jME3:</b><br>
+ * This Wrapper allows to share jME3 Images and VertexBuffers with OpenCL.<br>
+ * {@link com.jme3.scene.VertexBuffer} objects can be shared with OpenCL
+ * by calling {@link com.jme3.opencl.Context#bindVertexBuffer(com.jme3.scene.VertexBuffer, com.jme3.opencl.MemoryAccess) }
+ * resulting in a {@link com.jme3.opencl.Buffer} object. This buffer object
+ * can then be used as usual, allowing e.g. the dynamic modification of position buffers for particle systems.<br>
+ * {@link com.jme3.texture.Image} and {@link com.jme3.texture.Texture} objects can be used in OpenCL with the method
+ * {@link com.jme3.opencl.Context#bindImage(com.jme3.texture.Texture, com.jme3.opencl.MemoryAccess) }
+ * or variations of this method. The same holds for {@link com.jme3.texture.FrameBuffer.RenderBuffer} objects
+ * using {@link com.jme3.opencl.Context#bindRenderBuffer(com.jme3.texture.FrameBuffer.RenderBuffer, com.jme3.opencl.MemoryAccess) }.
+ * These methods result in an OpenCL-Image. Usages are e.g. animated textures,
+ * terrain based on height maps, post processing effects and so forth.
+ * <br>
+ * <i>Important:</i> Before shared objects can be used by any OpenCL function
+ * like kernel calls or read/write/copy methods, they must be aquired explicitly
+ * by {@link com.jme3.opencl.Buffer#acquireBufferForSharingAsync(com.jme3.opencl.CommandQueue) }
+ * or {@link com.jme3.opencl.Image#acquireImageForSharingAsync(com.jme3.opencl.CommandQueue) }.
+ * After the work is done, release the resource with
+ * {@link com.jme3.opencl.Buffer#releaseBufferForSharingAsync(com.jme3.opencl.CommandQueue) }
+ * or {@link com.jme3.opencl.Image#releaseImageForSharingAsync(com.jme3.opencl.CommandQueue) }.
+ * This ensures the synchronization of OpenCL and OpenGL.
+ * 
+ * <p>
+ * <b>Experts: choosing the right platform and devices</b><br>
+ * OpenCL can run on different platforms and different devices. On some systems,
+ * like multi-GPU setups, this choice really matters. To specify which platform
+ * and which devices are used, a custom implementation of 
+ * {@link com.jme3.opencl.PlatformChooser} can be used by calling
+ * {@link com.jme3.system.AppSettings#setOpenCLPlatformChooser(java.lang.Class) }.
+ * For more details, see the documentation of {@code PlatformChooser}.
+ * 
+ * <p>
+ * <b>Exception handling:</b><br>
+ * All OpenCL-wrapper classes in this package
+ * (this includes {@link com.jme3.opencl.Platform}, {@link com.jme3.opencl.Device},
+ * {@link com.jme3.opencl.Context}, {@link com.jme3.opencl.CommandQueue},
+ * {@link com.jme3.opencl.Buffer}, {@link com.jme3.opencl.Image},
+ * {@link com.jme3.opencl.Program}, {@link com.jme3.opencl.Kernel} and
+ * {@link com.jme3.opencl.Event})
+ * may throw the following exceptions in each method without being mentioned 
+ * explicetly in the documentation:
+ * <ul>
+ * <li>{@code NullPointerException}: one of the arguments is {@code null} and 
+ * {@code null} is not allowed</li>
+ * <li>{@code IllegalArgumentException}: the arguments don't follow the rules
+ * as specified in the documentation of the method, e.g. values are out of range
+ * or an array has the wrong size</li>
+ * <li>{@link com.jme3.opencl.OpenCLException}: some low-level exception was
+ * thrown. The exception always records the error code and error name and the 
+ * OpenCL function call where the error was detected. Please check the official
+ * OpenCL specification for the meanings of these errors for that particular function.</li>
+ * <li>{@code UnsupportedOperationException}: the OpenCL implementation does not
+ * support some operations. This is currently only an issue for Jogamp's Jogl
+ * renderer, since Jocl only supports OpenCL 1.1. LWJGL has full support for
+ * OpenCL 1.2 and 2.0.
+ * </ul>
+ */
+package com.jme3.opencl;
+
+//TODO: add profiling to Kernel, CommandQueue

+ 3 - 0
jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java

@@ -35,6 +35,7 @@ import com.jme3.material.RenderState;
 import com.jme3.material.RenderState.StencilOperation;
 import com.jme3.material.RenderState.TestFunction;
 import com.jme3.math.*;
+import com.jme3.opencl.OpenCLObjectManager;
 import com.jme3.renderer.*;
 import com.jme3.scene.Mesh;
 import com.jme3.scene.Mesh.Mode;
@@ -552,6 +553,7 @@ public final class GLRenderer implements Renderer {
     public void cleanup() {
         logger.log(Level.FINE, "Deleting objects and invalidating state");
         objManager.deleteAllObjects(this);
+        OpenCLObjectManager.getInstance().deleteAllObjects();
         statistics.clearMemory();
         invalidateState();
     }
@@ -935,6 +937,7 @@ public final class GLRenderer implements Renderer {
 
     public void postFrame() {
         objManager.deleteUnused(this);
+        OpenCLObjectManager.getInstance().deleteUnusedObjects();
         gl.resetStats();
     }
 

+ 34 - 1
jme3-core/src/main/java/com/jme3/system/AppSettings.java

@@ -31,6 +31,8 @@
  */
 package com.jme3.system;
 
+import com.jme3.opencl.DefaultPlatformChooser;
+import com.jme3.opencl.PlatformChooser;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -160,7 +162,9 @@ public final class AppSettings extends HashMap<String, Object> {
         defaults.put("GammaCorrection", false);
         defaults.put("Resizable", false);
         defaults.put("SwapBuffers", true);
-      //  defaults.put("Icons", null);
+        defaults.put("OpenCL", false);
+        defaults.put("OpenCLPlatformChooser", DefaultPlatformChooser.class.getName());
+        //  defaults.put("Icons", null);
     }
 
     /**
@@ -1019,4 +1023,33 @@ public final class AppSettings extends HashMap<String, Object> {
     public boolean isSwapBuffers() {
         return getBoolean("SwapBuffers");
     }
+
+    /**
+     * True to enable the creation of an OpenCL context.
+     *
+     * @param support
+     */
+    public void setOpenCLSupport(boolean support) {
+        putBoolean("OpenCL", support);
+    }
+
+    public boolean isOpenCLSupport() {
+        return getBoolean("OpenCL");
+    }
+    
+    /**
+     * Sets a custom platform chooser. This chooser specifies which platform and
+     * which devices are used for the OpenCL context.
+     * 
+     * Default: an implementation defined one.
+     * 
+     * @param chooser the class of the chooser, must have a default constructor
+     */
+    public void setOpenCLPlatformChooser(Class<? extends PlatformChooser> chooser) {
+        putString("OpenCLPlatformChooser", chooser.getName());
+    }
+    
+    public String getOpenCLPlatformChooser() {
+        return getString("OpenCLPlatformChooser");
+    }
 }

+ 5 - 0
jme3-core/src/main/java/com/jme3/system/JmeContext.java

@@ -110,6 +110,11 @@ public interface JmeContext {
      */
     public Renderer getRenderer();
 
+    /**
+     * @return The OpenCL context if available.
+     */
+    public com.jme3.opencl.Context getOpenCLContext();
+
     /**
      * @return Mouse input implementation. May be null if not available.
      */

+ 6 - 0
jme3-core/src/main/java/com/jme3/system/NullContext.java

@@ -37,6 +37,7 @@ import com.jme3.input.MouseInput;
 import com.jme3.input.TouchInput;
 import com.jme3.input.dummy.DummyKeyInput;
 import com.jme3.input.dummy.DummyMouseInput;
+import com.jme3.opencl.Context;
 import com.jme3.renderer.Renderer;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.logging.Level;
@@ -229,4 +230,9 @@ public class NullContext implements JmeContext, Runnable {
         return true; // Doesn't really matter if true or false. Either way
                      // RenderManager won't render anything.
     }
+
+    @Override
+    public Context getOpenCLContext() {
+        return null;
+    }
 }

+ 224 - 0
jme3-core/src/main/resources/Common/OpenCL/Matrix3f.clh

@@ -0,0 +1,224 @@
+
+#ifndef MATRIX3_H
+#define MATRIX3_H
+
+//Simple matrix library.
+//A 3x3 matrix is represented as a float16 in row major order
+
+typedef float16 mat3;
+
+//All matrix functions are prefixed with mat3 or mat4
+
+//Returns the zero matrix
+inline mat3 mat3Zero() {
+	return (float16)(0);
+}
+
+//Returns the identity matrix
+inline mat3 mat3Identity() {
+	return (float16)
+		(1, 0, 0, 0,
+		 0, 1, 0, 0,
+		 0, 0, 1, 0,
+		 0, 0, 0, 1);
+}
+
+inline mat3 mat3FromRows(float3 row1, float3 row2, float3 row3) {
+	return (float16) 
+		(row1.x, row1.y, row1.z, 0,
+		 row2.x, row2.y, row2.z, 0,
+		 row3.x, row3.y, row3.z, 0,
+		 0, 0, 0, 1);
+}
+
+inline mat3 mat3FromColumns(float3 col1, float3 col2, float3 col3) {
+	return (float16)
+		(col1.x, col2.x, col3.x, 0,
+		 col1.y, col2.y, col3.y, 0,
+		 col1.z, col2.z, col3.z, 0,
+		 0, 0, 0, 1);
+}
+
+inline mat3 mat3FromDiagonal(float3 diag) {
+	return (float16)
+		(diag.x, 0, 0, 0,
+		 0, diag.y, 0, 0,
+		 0, 0, diag.z, 0,
+		 0, 0, 0, 1);
+}
+
+//Returns the i-th row (0-based)
+inline float3 mat3GetRow(mat3 mat, int i) {
+	if (i==0) return mat.s012;
+	else if (i==1) return mat.s456;
+	else return mat.s89a;
+}
+
+//Sets the i-th row (0-based)
+inline mat3 mat3SetRow(mat3 mat, int i, float3 row) {
+	if (i==0) mat.s012 = row;
+	else if (i==1) mat.s456 = row;
+	else mat.s89a = row;
+	return mat;
+}
+
+//Returns the i-th column (0-based)
+inline float3 mat3GetColumn(mat3 mat, int i) {
+	if (i==0) return mat.s048;
+	else if (i==1) return mat.s159;
+	else return mat.s26a;
+}
+
+//Sets the i-th column (0-based)
+inline mat3 mat3SetColumn(mat3 mat, int i, float3 col) {
+	if (i==0) mat.s048 = col;
+	else if (i==1) mat.s159 = col;
+	else mat.s26a = col;
+	return mat;
+}
+
+//Returns the diagonal
+inline float3 mat3GetDiagonal(mat3 mat) {
+	return mat.s05a;
+}
+
+//Sets the diagonal
+inline mat3 mat3SetDiagonal(mat3 mat, float3 diag) {
+	mat.s05a = diag;
+	return mat;
+}
+
+mat3 mat3FromAngleNormalAxis(float angle, float3 axis) {
+	float fCos = cos(angle);
+	float fSin = sin(angle);
+	float fOneMinusCos = 1.0f - fCos;
+	float fX2 = axis.x * axis.x;
+	float fY2 = axis.y * axis.y;
+	float fZ2 = axis.z * axis.z;
+	float fXYM = axis.x * axis.y * fOneMinusCos;
+	float fXZM = axis.x * axis.z * fOneMinusCos;
+	float fYZM = axis.y * axis.z * fOneMinusCos;
+	float fXSin = axis.x * fSin;
+	float fYSin = axis.y * fSin;
+	float fZSin = axis.z * fSin;
+
+	return (float16) (
+		fX2 * fOneMinusCos + fCos,
+		fXYM - fZSin,
+		fXZM + fYSin,
+		0,
+		fXYM + fZSin,
+		fY2 * fOneMinusCos + fCos,
+		fYZM - fXSin,
+		0,
+		fXZM - fYSin,
+		fYZM + fXSin,
+		fZ2 * fOneMinusCos + fCos,
+		0,
+		0, 0, 0, 1
+	);
+}
+
+mat3 mat3FromAngleAxis(float angle, float3 axis) {
+	return mat3FromAngleNormalAxis(angle, normalize(axis));
+}
+
+//Multiplies the two matrices A and B
+inline mat3 mat3Mult(mat3 A, mat3 B) {
+	return (float16) (
+		dot(A.s012, B.s048),
+		dot(A.s012, B.s159),
+		dot(A.s012, B.s26a),
+		0,
+		dot(A.s456, B.s048),
+		dot(A.s456, B.s159),
+		dot(A.s456, B.s26a),
+		0,
+		dot(A.s89a, B.s048),
+		dot(A.s89a, B.s159),
+		dot(A.s89a, B.s26a),
+		0,
+		0, 0, 0, 1
+	);
+}
+
+//Computes Av (right multiply of a vector to a matrix)
+inline float3 mat3VMult(mat3 A, float3 v) {
+	return (float3) (
+		dot(A.s012, v),
+		dot(A.s456, v),
+		dot(A.s89a, v));
+}
+
+//Computes vA (left multiply of a vector to a matrix)
+inline float3 mat3VMult2(float3 v, mat3 A) {
+	return (float3) (
+		dot(v, A.s048),
+		dot(v, A.s159),
+		dot(v, A.s26a));
+}
+
+//Scales this matrix by a constant
+inline mat3 mat3Scale(mat3 mat, float s) {
+	return s*mat;
+}
+
+//Transposes this matrix
+inline mat3 mat3Transpose(mat3 mat) {
+	return mat.s048c159d26ae37bf; //magic
+}
+
+//Computes the determinant
+inline float mat3Determinant(mat3 mat) {
+	float fCo00 = mat.s5 * mat.sa - mat.s6 * mat.s9;
+	float fCo10 = mat.s6 * mat.s8 - mat.s4 * mat.sa;
+	float fCo20 = mat.s4 * mat.s9 - mat.s5 * mat.s8;
+	float fDet = mat.s0 * fCo00 + mat.s1 * fCo10 + mat.s2 * fCo20;
+	return fDet;
+}
+
+//Creates the adjoint
+inline mat3 mat3Adjoint(mat3 mat) {
+	return (float16) (
+		mat.s5 * mat.sa - mat.s6 * mat.s9,
+		mat.s2 * mat.s9 - mat.s1 * mat.sa,
+		mat.s1 * mat.s6 - mat.s2 * mat.s5,
+		0,
+		mat.s6 * mat.s8 - mat.s4 * mat.sa,
+		mat.s0 * mat.sa - mat.s2 * mat.s8,
+		mat.s2 * mat.s4 - mat.s0 * mat.s6,
+		0,
+		mat.s4 * mat.s9 - mat.s5 * mat.s8,
+		mat.s1 * mat.s8 - mat.s0 * mat.s9,
+		mat.s0 * mat.s5 - mat.s1 * mat.s4,
+		0,
+		0, 0, 0, 1
+	);
+}
+
+//Inverts this matrix
+inline mat3 mat3Invert(mat3 mat) {
+	float det = mat3Determinant(mat);
+	if (fabs(det) <= 1.1920928955078125E-7f) return mat3Zero();
+	mat3 m = mat3Adjoint(mat);
+	return m / det;
+}
+
+//Computes A+B
+inline mat3 mat3Add(mat3 A, mat3 B) {
+	return A + B;
+}
+
+inline bool mat3Equals(mat3 A, mat3 B, float epsilon) {
+	return fabs(A.s0 - B.s0)<epsilon
+		&& fabs(A.s1 - B.s1)<epsilon
+		&& fabs(A.s2 - B.s2)<epsilon
+		&& fabs(A.s4 - B.s4)<epsilon
+		&& fabs(A.s5 - B.s5)<epsilon
+		&& fabs(A.s6 - B.s6)<epsilon
+		&& fabs(A.s8 - B.s8)<epsilon
+		&& fabs(A.s9 - B.s9)<epsilon
+		&& fabs(A.sa - B.sa)<epsilon;
+}
+
+#endif

+ 319 - 0
jme3-core/src/main/resources/Common/OpenCL/Matrix4f.clh

@@ -0,0 +1,319 @@
+
+#ifndef MATRIX4_H
+#define MATRIX4_H
+
+//Simple matrix library.
+//A 4x4 matrix is represented as a float16 in row major order
+
+typedef float16 mat4;
+
+//All matrix functions are prefixed with mat3 or mat4
+
+//Returns the zero matrix
+inline mat4 mat4Zero() {
+	return (float16)(0);
+}
+
+//Returns the identity matrix
+inline mat4 mat4Identity() {
+	return (float16)
+		(1, 0, 0, 0,
+		 0, 1, 0, 0,
+		 0, 0, 1, 0,
+		 0, 0, 0, 1);
+}
+
+inline mat4 mat4FromRows(float4 row1, float4 row2, float4 row3, float4 row4) {
+	return (float16) (row1, row2, row3, row4);
+}
+
+inline mat4 mat4FromColumns(float4 col1, float4 col2, float4 col3, float4 col4) {
+	return (float16)
+		(col1.x, col2.x, col3.x, col4.x,
+		 col1.y, col2.y, col3.y, col4.y,
+		 col1.z, col2.z, col3.z, col4.z,
+		 col1.w, col2.w, col3.w, col4.w);
+}
+
+inline mat4 mat4FromDiagonal(float4 diag) {
+	return (float16)
+		(diag.x, 0, 0, 0,
+		 0, diag.y, 0, 0,
+		 0, 0, diag.z, 0,
+		 0, 0, 0, diag.w);
+}
+
+//Returns the i-th row (0-based)
+inline float4 mat4GetRow(mat4 mat, int i) {
+	if (i==0) return mat.s0123;
+	else if (i==1) return mat.s4567;
+	else if (i==2) return mat.s89ab;
+	else return mat.scdef;
+}
+
+//Sets the i-th row (0-based)
+inline mat4 mat4SetRow(mat4 mat, int i, float4 row) {
+	if (i==0) mat.s0123 = row;
+	else if (i==1) mat.s4567 = row;
+	else if (i==2) mat.s89ab = row;
+	else mat.scdef = row;
+	return mat;
+}
+
+//Returns the i-th column (0-based)
+inline float4 mat4GetColumn(mat4 mat, int i) {
+	if (i==0) return mat.s048c;
+	else if (i==1) return mat.s159d;
+	else if (i==2) return mat.s26ae;
+	else return mat.s37bf;
+}
+
+//Sets the i-th column (0-based)
+inline mat4 mat4SetColumn(mat4 mat, int i, float4 col) {
+	if (i==0) mat.s048c = col;
+	else if (i==1) mat.s159d = col;
+	else if (i==2) mat.s26ae = col;
+	else mat.s37bf = col;
+	return mat;
+}
+
+//Returns the diagonal
+inline float4 mat4GetDiagonal(mat4 mat) {
+	return mat.s05af;
+}
+
+//Sets the diagonal
+inline mat4 mat3SetDiagonal(mat4 mat, float4 diag) {
+	mat.s05af = diag;
+	return mat;
+}
+
+mat4 mat4FromFrame(float3 location, float3 direction, float3 up, float3 left)
+{
+	float3 fwdVector = direction;
+	float3 leftVector = cross(fwdVector, up);
+	float3 upVector = cross(leftVector, fwdVector);
+
+	return (float16) (
+		leftVector.x,
+		leftVector.y,
+		leftVector.z,
+		dot(-leftVector, location),
+
+		upVector.x,
+		upVector.y,
+		upVector.z,
+		dot(-upVector, location),
+
+		-fwdVector.x,
+		-fwdVector.y,
+		-fwdVector.z,
+		dot(fwdVector, location),
+
+		0, 0, 0, 1
+	);
+}
+
+inline mat4 mat4Transpose(mat4 mat) {
+	return mat.s048c159d26ae37bf; //magic
+}
+
+mat4 mat4FromAngleNormalAxis(float angle, float3 axis) {
+	float fCos = cos(angle);
+	float fSin = sin(angle);
+	float fOneMinusCos = 1.0f - fCos;
+	float fX2 = axis.x * axis.x;
+	float fY2 = axis.y * axis.y;
+	float fZ2 = axis.z * axis.z;
+	float fXYM = axis.x * axis.y * fOneMinusCos;
+	float fXZM = axis.x * axis.z * fOneMinusCos;
+	float fYZM = axis.y * axis.z * fOneMinusCos;
+	float fXSin = axis.x * fSin;
+	float fYSin = axis.y * fSin;
+	float fZSin = axis.z * fSin;
+
+	return (float16) (
+		fX2 * fOneMinusCos + fCos,
+		fXYM - fZSin,
+		fXZM + fYSin,
+		0,
+		fXYM + fZSin,
+		fY2 * fOneMinusCos + fCos,
+		fYZM - fXSin,
+		0,
+		fXZM - fYSin,
+		fYZM + fXSin,
+		fZ2 * fOneMinusCos + fCos,
+		0,
+		0, 0, 0, 1
+	);
+}
+
+mat4 mat4FromAngleAxis(float angle, float3 axis) {
+	return mat4FromAngleNormalAxis(angle, normalize(axis));
+}
+
+inline mat4 mat4Scale(mat4 mat, float s) {
+	return mat * s;
+}
+
+inline mat4 mat4Add(mat4 A, mat4 B) {
+	return A + B;
+}
+
+//Multiplies the two matrices A and B
+inline mat4 mat4Mult(mat4 A, mat4 B) {
+	return (float16) (
+		dot(A.s0123, B.s048c),
+		dot(A.s0123, B.s159d),
+		dot(A.s0123, B.s26ae),
+		dot(A.s0123, B.s37bf),
+
+		dot(A.s4567, B.s048c),
+		dot(A.s4567, B.s159d),
+		dot(A.s4567, B.s26ae),
+		dot(A.s4567, B.s37bf),
+
+		dot(A.s89ab, B.s048c),
+		dot(A.s89ab, B.s159d),
+		dot(A.s89ab, B.s26ae),
+		dot(A.s89ab, B.s37bf),
+
+		dot(A.scdef, B.s048c),
+		dot(A.scdef, B.s159d),
+		dot(A.scdef, B.s26ae),
+		dot(A.scdef, B.s37bf)
+	);
+}
+
+//Computes Av (right multiply of a vector to a matrix)
+inline float4 mat4VMult(mat4 A, float4 v) {
+	return (float4) (
+		dot(A.s0123, v),
+		dot(A.s4567, v),
+		dot(A.s89ab, v),
+		dot(A.scdef, v));
+}
+
+//Computes vA (left multiply of a vector to a matrix)
+inline float4 mat4VMult2(float4 v, mat4 A) {
+	return (float4) (
+		dot(v, A.s048c),
+		dot(v, A.s159d),
+		dot(v, A.s26ae),
+		dot(v, A.s37bf));
+}
+
+inline float4 mat4MultAcross(mat4 mat, float4 v) {
+	return mat4VMult2(v, mat);
+}
+
+inline float3 mat4MultNormal(mat4 mat, float3 v) {
+	return mat4VMult(mat, (float4)(v, 0)).xyz;
+}
+
+inline float3 mat4MultNormalAcross(mat4 mat, float3 v) {
+	return mat4VMult2((float4)(v, 0), mat).xyz;
+}
+
+mat4 mat4Invert(mat4 mat) {
+	float fA0 = mat.s0 * mat.s5 - mat.s1 * mat.s4;
+	float fA1 = mat.s0 * mat.s6 - mat.s2 * mat.s4;
+	float fA2 = mat.s0 * mat.s7 - mat.s3 * mat.s4;
+	float fA3 = mat.s1 * mat.s6 - mat.s2 * mat.s5;
+	float fA4 = mat.s1 * mat.s7 - mat.s3 * mat.s5;
+	float fA5 = mat.s2 * mat.s7 - mat.s3 * mat.s6;
+	float fB0 = mat.s8 * mat.sd - mat.s9 * mat.sc;
+	float fB1 = mat.s8 * mat.se - mat.sa * mat.sc;
+	float fB2 = mat.s8 * mat.sf - mat.sb * mat.sc;
+	float fB3 = mat.s9 * mat.se - mat.sa * mat.sd;
+	float fB4 = mat.s9 * mat.sf - mat.sb * mat.sd;
+	float fB5 = mat.sa * mat.sf - mat.sb * mat.se;
+	float fDet = fA0 * fB5 - fA1 * fB4 + fA2 * fB3 + fA3 * fB2 - fA4 * fB1 + fA5 * fB0;
+
+	if (fabs(fDet) <= 0.000001f) {
+		return mat4Zero();
+	}
+
+	mat4 store;
+	store.s0 = +mat.s5 * fB5 - mat.s6 * fB4 + mat.s7 * fB3;
+	store.s4 = -mat.s4 * fB5 + mat.s6 * fB2 - mat.s7 * fB1;
+	store.s8 = +mat.s4 * fB4 - mat.s5 * fB2 + mat.s7 * fB0;
+	store.sc = -mat.s4 * fB3 + mat.s5 * fB1 - mat.s6 * fB0;
+	store.s1 = -mat.s1 * fB5 + mat.s2 * fB4 - mat.s3 * fB3;
+	store.s5 = +mat.s0 * fB5 - mat.s2 * fB2 + mat.s3 * fB1;
+	store.s9 = -mat.s0 * fB4 + mat.s1 * fB2 - mat.s3 * fB0;
+	store.sd = +mat.s0 * fB3 - mat.s1 * fB1 + mat.s2 * fB0;
+	store.s2 = +mat.sd * fA5 - mat.se * fA4 + mat.sf * fA3;
+	store.s6 = -mat.sc * fA5 + mat.se * fA2 - mat.sf * fA1;
+	store.sa = +mat.sc * fA4 - mat.sd * fA2 + mat.sf * fA0;
+	store.se = -mat.sc * fA3 + mat.sd * fA1 - mat.se * fA0;
+	store.s3 = -mat.s9 * fA5 + mat.sa * fA4 - mat.sb * fA3;
+	store.s7 = +mat.s8 * fA5 - mat.sa * fA2 + mat.sb * fA1;
+	store.sb = -mat.s8 * fA4 + mat.s9 * fA2 - mat.sb * fA0;
+	store.sf = +mat.s8 * fA3 - mat.s9 * fA1 + mat.sa * fA0;
+
+	store /= fDet;
+
+	return store;
+}
+
+mat4 mat4Adjoint(mat4 mat) {
+	float fA0 = mat.s0 * mat.s5 - mat.s1 * mat.s4;
+	float fA1 = mat.s0 * mat.s6 - mat.s2 * mat.s4;
+	float fA2 = mat.s0 * mat.s7 - mat.s3 * mat.s4;
+	float fA3 = mat.s1 * mat.s6 - mat.s2 * mat.s5;
+	float fA4 = mat.s1 * mat.s7 - mat.s3 * mat.s5;
+	float fA5 = mat.s2 * mat.s7 - mat.s3 * mat.s6;
+	float fB0 = mat.s8 * mat.sd - mat.s9 * mat.sc;
+	float fB1 = mat.s8 * mat.se - mat.sa * mat.sc;
+	float fB2 = mat.s8 * mat.sf - mat.sb * mat.sc;
+	float fB3 = mat.s9 * mat.se - mat.sa * mat.sd;
+	float fB4 = mat.s9 * mat.sf - mat.sb * mat.sd;
+	float fB5 = mat.sa * mat.sf - mat.sb * mat.se;
+
+	mat4 store;
+
+	store.s0 = +mat.s5 * fB5 - mat.s6 * fB4 + mat.s7 * fB3;
+	store.s4 = -mat.s4 * fB5 + mat.s6 * fB2 - mat.s7 * fB1;
+	store.s8 = +mat.s4 * fB4 - mat.s5 * fB2 + mat.s7 * fB0;
+	store.sc = -mat.s4 * fB3 + mat.s5 * fB1 - mat.s6 * fB0;
+	store.s1 = -mat.s1 * fB5 + mat.s2 * fB4 - mat.s3 * fB3;
+	store.s5 = +mat.s0 * fB5 - mat.s2 * fB2 + mat.s3 * fB1;
+	store.s9 = -mat.s0 * fB4 + mat.s1 * fB2 - mat.s3 * fB0;
+	store.sd = +mat.s0 * fB3 - mat.s1 * fB1 + mat.s2 * fB0;
+	store.s2 = +mat.sd * fA5 - mat.se * fA4 + mat.sf * fA3;
+	store.s6 = -mat.sc * fA5 + mat.se * fA2 - mat.sf * fA1;
+	store.sa = +mat.sc * fA4 - mat.sd * fA2 + mat.sf * fA0;
+	store.se = -mat.sc * fA3 + mat.sd * fA1 - mat.se * fA0;
+	store.s3 = -mat.s9 * fA5 + mat.sa * fA4 - mat.sb * fA3;
+	store.s7 = +mat.s8 * fA5 - mat.sa * fA2 + mat.sb * fA1;
+	store.sb = -mat.s8 * fA4 + mat.s9 * fA2 - mat.sb * fA0;
+	store.sf = +mat.s8 * fA3 - mat.s9 * fA1 + mat.sa * fA0;
+
+	return store;
+}
+
+float mat4Determinant(mat4 mat) {
+	float fA0 = mat.s0 * mat.s5 - mat.s1 * mat.s4;
+	float fA1 = mat.s0 * mat.s6 - mat.s2 * mat.s4;
+	float fA2 = mat.s0 * mat.s7 - mat.s3 * mat.s4;
+	float fA3 = mat.s1 * mat.s6 - mat.s2 * mat.s5;
+	float fA4 = mat.s1 * mat.s7 - mat.s3 * mat.s5;
+	float fA5 = mat.s2 * mat.s7 - mat.s3 * mat.s6;
+	float fB0 = mat.s8 * mat.sd - mat.s9 * mat.sc;
+	float fB1 = mat.s8 * mat.se - mat.sa * mat.sc;
+	float fB2 = mat.s8 * mat.sf - mat.sb * mat.sc;
+	float fB3 = mat.s9 * mat.se - mat.sa * mat.sd;
+	float fB4 = mat.s9 * mat.sf - mat.sb * mat.sd;
+	float fB5 = mat.sa * mat.sf - mat.sb * mat.se;
+	float fDet = fA0 * fB5 - fA1 * fB4 + fA2 * fB3 + fA3 * fB2 - fA4 * fB1 + fA5 * fB0;
+	return fDet;
+}
+
+inline bool mat4Equals(mat4 A, mat4 B, float epsilon) {
+	return all(isless(fabs(A - B), epsilon));
+}
+
+
+#endif

+ 185 - 0
jme3-core/src/main/resources/Common/OpenCL/Random.clh

@@ -0,0 +1,185 @@
+//This is a port of java.util.Random to OpenCL
+
+//Because not all devices support doubles, the double returning functions 
+//must be explicit activated with the following preprocessor macro:
+//#define RANDOM_DOUBLES
+
+#ifdef RANDOM_DOUBLES
+//#ifdef cl_khr_fp64
+#pragma OPENCL EXTENSION cl_khr_fp64 : enable
+//#elif defined(cl_amd_fp64)
+//#pragma OPENCL EXTENSION cl_amd_fp64 : enable
+//#else
+//#error "Double precision floating point not supported by OpenCL implementation."
+//#endif
+#endif
+
+inline int randNext(int bits, __global ulong* seed)
+{
+	*seed = (*seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
+	return (int)(*seed >> (48 - bits));
+}
+
+/**
+ * Retrieves the next random integer value.
+ * The buffer used as seed must be read-write.
+ * Usage: 
+ * <code>
+ *  __kernel void TestRandom(__global ulong* seeds) {
+ *    // ...
+ *    int i = randInt(seeds + get_global_id(0));
+ *    // ---
+ * }
+ * </code>
+ */
+inline int randInt(__global ulong* seed) {
+	return randNext(32, seed);
+}
+
+/**
+ * Retrieves the next random integer value between 0 (inclusive) and n (exclusive).
+ * The buffer used as seed must be read-write.
+ * Usage: 
+ * <code>
+ *  __kernel void TestRandom(__global ulong* seeds) {
+ *    // ...
+ *    int i = randIntN(n, seeds + get_global_id(0));
+ *    // ---
+ * }
+ * </code>
+ */
+inline int randIntN(int n, __global ulong* seed) {
+	if (n <= 0)
+		return 0;
+
+	if ((n & -n) == n)  // i.e., n is a power of 2
+		return (int)((n * (long)randNext(31, seed)) >> 31);
+
+	int bits, val;
+	do {
+		bits = randNext(31, seed);
+		val = bits % n;
+	} while (bits - val + (n-1) < 0);
+	return val;
+}
+
+/**
+ * Retrieves the next random long value.
+ * The buffer used as seed must be read-write.
+ * Usage: 
+ * <code>
+ *  __kernel void TestRandom(__global ulong* seeds) {
+ *    // ...
+ *    long l = randLong(seeds + get_global_id(0));
+ *    // ---
+ * }
+ * </code>
+ */
+inline long randLong(__global ulong* seed) {
+	// it's okay that the bottom word remains signed.
+	return ((long)(randNext(32, seed)) << 32) + randNext(32, seed);
+}
+
+/**
+ * Retrieves the next random boolean value.
+ * The buffer used as seed must be read-write.
+ * Usage: 
+ * <code>
+ *  __kernel void TestRandom(__global ulong* seeds) {
+ *    // ...
+ *    bool b = randBool(seeds + get_global_id(0));
+ *    // ---
+ * }
+ * </code>
+ */
+inline bool randBool(__global ulong* seed) {
+	return randNext(1, seed) != 0;
+}
+
+#ifdef RANDOM_DOUBLES
+/**
+ * Retrieves the next random double value.
+ * The buffer used as seed must be read-write.
+ * To use this function, the preprocessor define RANDOM_DOUBLES must be set.
+ * Usage: 
+ * <code>
+ *  __kernel void TestRandom(__global ulong* seeds) {
+ *    // ...
+ *    double d = randDouble(seeds + get_global_id(0));
+ *    // ---
+ * }
+ * </code>
+ */
+inline double randDouble(__global ulong* seed) {
+	return (((long)(randNext(26, seed)) << 27) + randNext(27, seed))
+		/ (double)(1L << 53);
+}
+#endif
+
+/**
+ * Retrieves the next random float value.
+ * The buffer used as seed must be read-write.
+ * Usage: 
+ * <code>
+ *  __kernel void TestRandom(__global ulong* seeds) {
+ *    // ...
+ *    float f = randFloat(seeds + get_global_id(0));
+ *    // ---
+ * }
+ * </code>
+ */
+inline float randFloat(__global ulong* seed)
+{
+	return randNext(24, seed) / ((float)(1 << 24));
+}
+
+/**
+ * Retrieves the next random float values with a gaussian distribution of mean 0
+ * and derivation 1.
+ * The buffer used as seed must be read-write.
+ * Usage: 
+ * <code>
+ *  __kernel void TestRandom(__global ulong* seeds) {
+ *    // ...
+ *    float2 f2 = randGausianf(seeds + get_global_id(0));
+ *    // ---
+ * }
+ * </code>
+ */
+inline float2 randGaussianf(__global ulong* seed) {
+	float v1, v2, s;
+	do {
+		v1 = 2 * randFloat(seed) - 1; // between -1 and 1
+		v2 = 2 * randFloat(seed) - 1; // between -1 and 1
+		s = v1 * v1 + v2 * v2;
+	} while (s >= 1 || s == 0);
+	float multiplier = sqrt(-2 * log(s)/s);
+	return (float2) (v1 * multiplier, v2 * multiplier);
+}
+
+#ifdef RANDOM_DOUBLES
+/**
+ * Retrieves the next random double values with a gaussian distribution of mean 0
+ * and derivation 1.
+ * The buffer used as seed must be read-write.
+ * To use this function, the preprocessor define RANDOM_DOUBLES must be set.
+ * Usage: 
+ * <code>
+ *  __kernel void TestRandom(__global ulong* seeds) {
+ *    // ...
+ *    double2 f2 = randGausian(seeds + get_global_id(0));
+ *    // ---
+ * }
+ * </code>
+ */
+inline double2 randGaussian(__global ulong* seed) {
+	double v1, v2, s;
+	do {
+		v1 = 2 * randDouble(seed) - 1; // between -1 and 1
+		v2 = 2 * randDouble(seed) - 1; // between -1 and 1
+		s = v1 * v1 + v2 * v2;
+	} while (s >= 1 || s == 0);
+	double multiplier = sqrt(-2 * log(s)/s);
+	return (double2) (v1 * multiplier, v2 * multiplier);
+}
+#endif

+ 6 - 0
jme3-desktop/src/main/java/com/jme3/system/awt/AwtPanelsContext.java

@@ -37,6 +37,7 @@ import com.jme3.input.MouseInput;
 import com.jme3.input.TouchInput;
 import com.jme3.input.awt.AwtKeyInput;
 import com.jme3.input.awt.AwtMouseInput;
+import com.jme3.opencl.Context;
 import com.jme3.renderer.Renderer;
 import com.jme3.system.*;
 import java.util.ArrayList;
@@ -145,6 +146,11 @@ public class AwtPanelsContext implements JmeContext {
         return actualContext != null && actualContext.isRenderable();
     }
 
+    @Override
+    public Context getOpenCLContext() {
+        return actualContext.getOpenCLContext();
+    }
+    
     public AwtPanelsContext(){
     }
 

+ 1 - 0
jme3-examples/build.gradle

@@ -32,6 +32,7 @@ dependencies {
     compile project(':jme3-jogg')
     compile project(':jme3-jogl')
     compile project(':jme3-lwjgl')
+//    compile project(':jme3-lwjgl3')
     compile project(':jme3-networking')
     compile project(':jme3-niftygui')
     compile project(':jme3-plugins')

+ 311 - 0
jme3-examples/src/main/java/jme3test/opencl/HelloOpenCL.java

@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.opencl;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.font.BitmapFont;
+import com.jme3.font.BitmapText;
+import com.jme3.math.ColorRGBA;
+import com.jme3.opencl.*;
+import com.jme3.system.AppSettings;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Simple test checking if the basic functions of the OpenCL wrapper work
+ * @author shaman
+ */
+public class HelloOpenCL extends SimpleApplication {
+    private static final Logger LOG = Logger.getLogger(HelloOpenCL.class.getName());
+
+    public static void main(String[] args){
+        HelloOpenCL app = new HelloOpenCL();
+        AppSettings settings = new AppSettings(true);
+        settings.setOpenCLSupport(true);
+        settings.setVSync(true);
+//        settings.setRenderer(AppSettings.JOGL_OPENGL_FORWARD_COMPATIBLE);
+        app.setSettings(settings);
+        app.start(); // start the game
+    }
+
+    @Override
+    public void simpleInitApp() {
+        BitmapFont fnt = assetManager.loadFont("Interface/Fonts/Default.fnt");
+        Context clContext = context.getOpenCLContext();
+        if (clContext == null) {
+            BitmapText txt = new BitmapText(fnt);
+            txt.setText("No OpenCL Context created!\nSee output log for details.");
+            txt.setLocalTranslation(5, settings.getHeight() - 5, 0);
+            guiNode.attachChild(txt);
+            return;
+        }
+        CommandQueue clQueue = clContext.createQueue();
+        
+        StringBuilder str = new StringBuilder();
+        str.append("OpenCL Context created:\n  Platform: ")
+                .append(clContext.getDevices().get(0).getPlatform().getName())
+                .append("\n  Devices: ").append(clContext.getDevices());
+        str.append("\nTests:");
+        str.append("\n  Buffers: ").append(testBuffer(clContext, clQueue));
+        str.append("\n  Kernel: ").append(testKernel(clContext, clQueue));
+        str.append("\n  Images: ").append(testImages(clContext, clQueue));
+        
+        clQueue.release();
+        
+        BitmapText txt1 = new BitmapText(fnt);
+        txt1.setText(str.toString());
+        txt1.setLocalTranslation(5, settings.getHeight() - 5, 0);
+        guiNode.attachChild(txt1);
+        
+        flyCam.setEnabled(false);
+        inputManager.setCursorVisible(true);
+    }
+    
+    private static void assertEquals(byte expected, byte actual, String message) {
+        if (expected != actual) {
+            System.err.println(message+": expected="+expected+", actual="+actual);
+            throw new AssertionError();
+        }
+    }
+    private static void assertEquals(long expected, long actual, String message) {
+        if (expected != actual) {
+            System.err.println(message+": expected="+expected+", actual="+actual);
+            throw new AssertionError();
+        }
+    }
+    private static void assertEquals(double expected, double actual, String message) {
+        if (Math.abs(expected - actual) >= 0.00001) {
+            System.err.println(message+": expected="+expected+", actual="+actual);
+            throw new AssertionError();
+        }
+    }
+    private static void assertEquals(Object expected, Object actual, String message) {
+        if (!Objects.equals(expected, actual)) {
+            System.err.println(message+": expected="+expected+", actual="+actual);
+            throw new AssertionError();
+        }
+    }
+    
+    private boolean testBuffer(Context clContext, CommandQueue clQueue) {
+        try {
+            //create two buffers
+            ByteBuffer h1 = BufferUtils.createByteBuffer(256);
+            Buffer b1 = clContext.createBuffer(256);
+            ByteBuffer h2 = BufferUtils.createByteBuffer(256);
+            Buffer b2 = clContext.createBuffer(256);
+
+            //fill buffer
+            h2.rewind();
+            for (int i=0; i<256; ++i) {
+                h2.put((byte)i);
+            }
+            h2.rewind();
+            b2.write(clQueue, h2);
+            
+            //copy b2 to b1
+            b2.copyTo(clQueue, b1);
+            
+            //read buffer
+            h1.rewind();
+            b1.read(clQueue, h1);
+            h1.rewind();
+            for (int i=0; i<256; ++i) {
+                byte b = h1.get();
+                assertEquals((byte) i, b, "Wrong byte read");
+            }
+            
+            //read buffer with offset
+            int low = 26;
+            int high = 184;
+            h1.position(5);
+            Event event = b1.readAsync(clQueue, h1, high-low, low);
+            event.waitForFinished();
+            h1.position(5);
+            for (int i=0; i<high-low; ++i) {
+                byte b = h1.get();
+                assertEquals((byte) (i+low), b, "Wrong byte read");
+            }
+        
+            //release
+            b1.release();
+            b2.release();
+            
+        } catch (AssertionError ex) {
+            LOG.log(Level.SEVERE, "Buffer test failed with an assertion error");
+            return false;
+        } catch (Exception ex) {
+            LOG.log(Level.SEVERE, "Buffer test failed with:", ex);
+            return false;
+        }
+        return true;
+    }
+    
+    private boolean testKernel(Context clContext, CommandQueue clQueue) {
+        try {
+            //create fill code
+            String include = "#define TYPE float\n";
+            Program program = clContext.createProgramFromSourceFilesWithInclude(assetManager, include, "jme3test/opencl/Blas.cl");
+            program.build();
+            Kernel kernel = program.createKernel("Fill");
+            System.out.println("number of args: "+kernel.getArgCount());
+
+            //fill buffer
+            int size = 256+128;
+            Buffer buffer = clContext.createBuffer(size*4);
+            float value = 5;
+            Event event = kernel.Run1(clQueue, new com.jme3.opencl.Kernel.WorkSize(buffer.getSize() / 4), buffer, value);
+            event.waitForFinished();
+            
+            //check if filled
+            ByteBuffer buf = buffer.map(clQueue, MappingAccess.MAP_READ_ONLY);
+            FloatBuffer buff = buf.asFloatBuffer();
+            for (int i=0; i<size; ++i) {
+                float v = buff.get(i);
+                assertEquals(value, v, "Buffer filled with the wrong value at index "+i);
+            }
+            buffer.unmap(clQueue, buf);
+            
+            //release
+            buffer.release();
+            kernel.release();
+            program.release();
+
+        } catch (AssertionError ex) {
+            LOG.log(Level.SEVERE, "kernel test failed with an assertion error");
+            return false;
+        } catch (Exception ex) {
+            LOG.log(Level.SEVERE, "kernel test failed with:", ex);
+            return false;
+        }
+        return true;
+    }
+    
+    private boolean testImages(Context clContext, CommandQueue clQueue) {
+        try {
+            //query supported formats
+            for (MemoryAccess ma : MemoryAccess.values()) {
+                for (Image.ImageType type : Image.ImageType.values()) {
+                    try {
+                        System.out.println("Formats for " + ma + " and " + type + ": " + Arrays.toString(clContext.querySupportedFormats(ma, type)));
+                    } catch (UnsupportedOperationException e) {
+                        LOG.warning(e.getLocalizedMessage());
+                    }
+                }
+            }
+            
+            //create an image
+            Image.ImageFormat format = new Image.ImageFormat(Image.ImageChannelOrder.RGBA, Image.ImageChannelType.FLOAT);
+            Image.ImageDescriptor descr = new Image.ImageDescriptor(Image.ImageType.IMAGE_2D, 1920, 1080, 0, 0);
+            Image image = clContext.createImage(MemoryAccess.READ_WRITE, format, descr);
+            System.out.println("image created");
+            
+            //check queries
+            assertEquals(descr.type, image.getImageType(), "Wrong image type");
+            assertEquals(format, image.getImageFormat(), "Wrong image format");
+            assertEquals(descr.width, image.getWidth(), "Wrong width");
+            assertEquals(descr.height, image.getHeight(), "Wrong height");
+            
+            //fill with red and blue
+            ColorRGBA color1 = ColorRGBA.Red;
+            ColorRGBA color2 = ColorRGBA.Blue;
+            Event e1 = image.fillAsync(clQueue, new long[]{0,0,0}, new long[]{descr.width/2, descr.height, 1}, color1);
+            Event e2 = image.fillAsync(clQueue, new long[]{descr.width/2,0,0}, new long[]{descr.width/2, descr.height, 1}, color2);
+            e1.waitForFinished();
+            e2.waitForFinished();
+            
+            //copy to a buffer
+            Buffer buffer = clContext.createBuffer(4*4*500*1024);
+            Event e3 = image.copyToBufferAsync(clQueue, buffer, new long[]{10,10,0}, new long[]{500,1024,1}, 0);
+            e3.release();
+            //this buffer must be completely red
+            ByteBuffer map1 = buffer.map(clQueue, MappingAccess.MAP_READ_ONLY);
+            FloatBuffer map1F = map1.asFloatBuffer(); map1F.rewind();
+            for (int x=0; x<500; ++x) {
+                for (int y=0; y<1024; ++y) {
+                    float r = map1F.get(); 
+                    float g = map1F.get(); 
+                    float b = map1F.get(); 
+                    float a = map1F.get();
+                    assertEquals(1, r, "Wrong red component");
+                    assertEquals(0, g, "Wrong green component");
+                    assertEquals(0, b, "Wrong blue component");
+                    assertEquals(1, a, "Wrong alpha component");
+                }
+            }
+            buffer.unmap(clQueue, map1);
+            
+            //create a second image
+            format = new Image.ImageFormat(Image.ImageChannelOrder.RGBA, Image.ImageChannelType.FLOAT);
+            descr = new Image.ImageDescriptor(Image.ImageType.IMAGE_2D, 512, 512, 0, 0);
+            Image image2 = clContext.createImage(MemoryAccess.READ_WRITE, format, descr);
+            //copy an area of image1 to image2
+            image.copyTo(clQueue, image2, new long[]{1000, 20,0}, new long[]{0,0,0}, new long[]{512, 512,1});
+            //this area should be completely blue
+            Image.ImageMapping map2 = image2.map(clQueue, new long[]{0,0,0}, new long[]{512,512,1}, MappingAccess.MAP_READ_WRITE);
+            FloatBuffer map2F = map2.buffer.asFloatBuffer();
+            for (int y=0; y<512; ++y) {
+                for (int x=0; x<512; ++x) {
+                    long index = 4 * x + y * (map2.rowPitch / 4);
+                    map2F.position((int) index);
+                    float r = map2F.get();
+                    float g = map2F.get();
+                    float b = map2F.get();
+                    float a = map2F.get();
+                    assertEquals(0, r, "Wrong red component");
+                    assertEquals(0, g, "Wrong green component");
+                    assertEquals(1, b, "Wrong blue component");
+                    assertEquals(1, a, "Wrong alpha component");
+                }
+            }
+            image2.unmap(clQueue, map2);
+            
+            //release
+            image.release();
+            image2.release();
+            buffer.release();
+            
+        } catch (AssertionError ex) {
+            LOG.log(Level.SEVERE, "image test failed with an assertion error");
+            return false;
+        } catch (Exception ex) {
+            LOG.log(Level.SEVERE, "image test failed with:", ex);
+            return false;
+        }
+        return true;
+    }
+}

+ 255 - 0
jme3-examples/src/main/java/jme3test/opencl/TestContextSwitching.java

@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.opencl;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.niftygui.NiftyJmeDisplay;
+import com.jme3.opencl.*;
+import com.jme3.system.AppSettings;
+import de.lessvoid.nifty.Nifty;
+import de.lessvoid.nifty.NiftyEventSubscriber;
+import de.lessvoid.nifty.controls.*;
+import de.lessvoid.nifty.screen.Screen;
+import de.lessvoid.nifty.screen.ScreenController;
+import java.util.Collections;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Sebastian Weiss
+ */
+public class TestContextSwitching extends SimpleApplication implements ScreenController {
+    private static final Logger LOG = Logger.getLogger(TestContextSwitching.class.getName());
+    
+    private Nifty nifty;
+    private Label infoLabel;
+    private Button applyButton;
+    private ListBox<String> platformListBox;
+    private ListBox<String> deviceListBox;
+    
+    private static String selectedPlatform;
+    private static String selectedDevice;
+    private Context clContext;
+    private static List<? extends Platform> availabePlatforms;
+    private Buffer testBuffer;
+    private boolean bufferCreated;
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        new TestContextSwitching().start();
+    }
+
+    public TestContextSwitching() {
+        AppSettings settings = new AppSettings(true);
+        settings.setOpenCLSupport(true);
+        settings.setVSync(true);
+        settings.setWidth(800);
+        settings.setHeight(600);
+        settings.setOpenCLPlatformChooser(CustomPlatformChooser.class);
+        //settings.setRenderer(AppSettings.JOGL_OPENGL_FORWARD_COMPATIBLE);
+        
+        setSettings(settings);
+        setShowSettings(false);
+    }
+
+    @Override
+    public void simpleInitApp() {
+        
+        clContext = null;
+        
+        NiftyJmeDisplay niftyDisplay = NiftyJmeDisplay.newNiftyJmeDisplay(
+                assetManager,
+                inputManager,
+                audioRenderer,
+                guiViewPort);
+        nifty = niftyDisplay.getNifty();
+        nifty.fromXml("jme3test/opencl/ContextSwitchingScreen.xml", "Screen", this);
+        guiViewPort.addProcessor(niftyDisplay);
+        inputManager.setCursorVisible(true);
+        flyCam.setEnabled(false);
+    }
+
+    @Override
+    public void simpleUpdate(float tpf) {
+        if (applyButton != null) {
+            updateInfos();
+        }
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void bind(Nifty nifty, Screen screen) {
+        applyButton = screen.findNiftyControl("ApplyButton", Button.class);
+        platformListBox = screen.findNiftyControl("PlatformListBox", ListBox.class);
+        deviceListBox = screen.findNiftyControl("DeviceListBox", ListBox.class);
+        infoLabel = screen.findNiftyControl("InfoLabel", Label.class);
+        
+        updateInfos();
+        
+        platformListBox.clear();
+        for (Platform p : availabePlatforms) {
+            platformListBox.addItem(p.getName());
+        }
+        platformListBox.selectItem(selectedPlatform);
+        changePlatform(selectedPlatform);
+    }
+    
+    private void updateInfos() {
+        
+        if (testBuffer == null && clContext != null && !bufferCreated) {
+            try {
+                testBuffer = clContext.createBuffer(1024);
+                testBuffer.register();
+                LOG.info("Test buffer created");
+            } catch (OpenCLException ex) {
+                LOG.log(Level.SEVERE, "Unable to create buffer", ex);
+            }
+            bufferCreated = true;
+        }
+        
+        Context c = context.getOpenCLContext();
+        if (c == clContext) {
+            return;
+        }
+        clContext = c;
+        LOG.info("context changed");
+        testBuffer = null;
+        bufferCreated = false;
+        StringBuilder text = new StringBuilder();
+        text.append("Current context:\n");
+        text.append("  Platform: ").append(clContext.getDevices().get(0).getPlatform().getName()).append("\n");
+        text.append("  Device: ").append(clContext.getDevices().get(0).getName()).append("\n");
+        text.append("  Profile: ").append(clContext.getDevices().get(0).getProfile()).append("\n");
+        text.append("  Memory: ").append(clContext.getDevices().get(0).getGlobalMemorySize()).append(" B\n");
+        text.append("  Compute Units: ").append(clContext.getDevices().get(0).getComputeUnits()).append("\n");
+        infoLabel.setText(text.toString());
+    }
+
+    @NiftyEventSubscriber(id="ApplyButton")
+    public void onButton(String id, ButtonClickedEvent event) {
+        LOG.log(Level.INFO, "Change context: platorm={0}, device={1}", new Object[]{selectedPlatform, selectedDevice});
+        restart();
+    }
+    
+    private void changePlatform(String platform) {
+        selectedPlatform = platform;
+        Platform p = null;
+        for (Platform p2 : availabePlatforms) {
+            if (p2.getName().equals(selectedPlatform)) {
+                p = p2;
+                break;
+            }
+        }
+        deviceListBox.clear();
+        if (p == null) {
+            return;
+        }
+        for (Device d : p.getDevices()) {
+            deviceListBox.addItem(d.getName());
+        }
+        deviceListBox.selectItem(selectedDevice);
+    }
+    
+    @NiftyEventSubscriber(id="PlatformListBox")
+    public void onPlatformChanged(String id, ListBoxSelectionChangedEvent<String> event) {
+        String p = event.getSelection().isEmpty() ? null : event.getSelection().get(0);
+        LOG.log(Level.INFO, "Selected platform changed to {0}", p);
+        selectedPlatform = p;
+        changePlatform(p);
+    }
+    
+    @NiftyEventSubscriber(id="DeviceListBox")
+    public void onDeviceChanged(String id, ListBoxSelectionChangedEvent<String> event) {
+        String d = event.getSelection().isEmpty() ? null : event.getSelection().get(0);
+        LOG.log(Level.INFO, "Selected device changed to {0}", d);
+        selectedDevice = d;
+    }
+    
+    @Override
+    public void onStartScreen() {
+        
+    }
+
+    @Override
+    public void onEndScreen() {
+        
+    }
+    
+    public static class CustomPlatformChooser implements PlatformChooser {
+
+        public CustomPlatformChooser() {}
+        
+        @Override
+        public List<? extends Device> chooseDevices(List<? extends Platform> platforms) {
+            availabePlatforms = platforms;
+            
+            Platform platform = null;
+            for (Platform p : platforms) {
+                if (p.getName().equals(selectedPlatform)) {
+                    platform = p;
+                    break;
+                }
+            }
+            if (platform == null) {
+                platform = platforms.get(0);
+            }
+            selectedPlatform = platform.getName();
+            
+            Device device = null;
+            for (Device d : platform.getDevices()) {
+                if (d.getName().equals(selectedDevice)) {
+                    device = d;
+                    break;
+                }
+            }
+            if (device == null) {
+                for (Device d : platform.getDevices()) {
+                    if (d.getDeviceType() == Device.DeviceType.GPU) {
+                        device = d;
+                        break;
+                    }
+                }
+            }
+            if (device == null) {
+                device = platform.getDevices().get(0);
+            }
+            selectedDevice = device.getName();
+            
+            return Collections.singletonList(device);
+        }
+        
+    }
+}

+ 174 - 0
jme3-examples/src/main/java/jme3test/opencl/TestMultipleApplications.java

@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.opencl;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.font.BitmapFont;
+import com.jme3.font.BitmapText;
+import com.jme3.font.Rectangle;
+import com.jme3.opencl.*;
+import com.jme3.system.AppSettings;
+import java.util.Collections;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class creates multiple instances of {@link TestVertexBufferSharing}.
+ * This is used to test if multiple opencl instances can run in parallel.
+ * @author Sebastian Weiss
+ */
+public class TestMultipleApplications extends SimpleApplication {
+    private static final Logger LOG = Logger.getLogger(TestMultipleApplications.class.getName());
+    
+    private static final Object sync = new Object();
+    private static Platform selectedPlatform;
+    private static List<? extends Device> availableDevices;
+    private static int currentDeviceIndex;
+    
+    private Context clContext;
+    private CommandQueue clQueue;
+    private Kernel kernel;
+    private Buffer buffer;
+    private boolean failed;
+    
+    private BitmapText infoText;
+    private BitmapText statusText;
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        final AppSettings settings = new AppSettings(true);
+        settings.setOpenCLSupport(true);
+        settings.setVSync(true);
+        settings.setOpenCLPlatformChooser(CustomPlatformChooser.class);
+        settings.setRenderer(AppSettings.JOGL_OPENGL_FORWARD_COMPATIBLE);
+        for (int i=0; i<2; ++i) {
+            new Thread() {
+                public void run() {
+                    if (currentDeviceIndex == -1) {
+                        return;
+                    }
+                    TestMultipleApplications app = new TestMultipleApplications();
+                    app.setSettings(settings);
+                    app.setShowSettings(false);
+                    app.start();
+                }
+            }.start();
+        }
+    }
+    
+    public static class CustomPlatformChooser implements PlatformChooser {
+
+        public CustomPlatformChooser() {}
+        
+        @Override
+        public List<? extends Device> chooseDevices(List<? extends Platform> platforms) {
+            synchronized(sync) {
+            if (currentDeviceIndex == -1) {
+                return Collections.emptyList();
+            }
+
+            Platform platform = platforms.get(0);
+            availableDevices = platform.getDevices();
+            selectedPlatform = platform;
+            
+            Device device = platform.getDevices().get(currentDeviceIndex);
+            currentDeviceIndex ++;
+            if (currentDeviceIndex >= availableDevices.size()) {
+                currentDeviceIndex = -1;
+            }
+            
+            return Collections.singletonList(device);
+            }
+        }
+        
+    }
+    
+    @Override
+    public void simpleInitApp() {
+        clContext = context.getOpenCLContext();
+        if (clContext == null) {
+            LOG.severe("No OpenCL context found");
+            stop();
+            return;
+        }
+        Device device = clContext.getDevices().get(0);
+        clQueue = clContext.createQueue(device);
+        clQueue.register();
+        
+        String source = ""
+                + "__kernel void Fill(__global float* vb, float v)\n"
+                + "{\n"
+                + "  int idx = get_global_id(0);\n"
+                + "  vb[idx] = v;\n"
+                + "}\n";
+        Program program = clContext.createProgramFromSourceCode(source);
+        program.build();
+        program.register();
+        kernel = program.createKernel("Fill");
+        kernel.register();
+        
+        buffer = clContext.createBuffer(4);
+        buffer.register();
+        
+        flyCam.setEnabled(false);
+        inputManager.setCursorVisible(true);
+        
+        BitmapFont fnt = assetManager.loadFont("Interface/Fonts/Default.fnt");
+        infoText = new BitmapText(fnt, false);
+        //infoText.setBox(new Rectangle(0, 0, settings.getWidth(), settings.getHeight()));
+        infoText.setText("Device: "+clContext.getDevices());
+        infoText.setLocalTranslation(0, settings.getHeight(), 0);
+        guiNode.attachChild(infoText);
+        statusText = new BitmapText(fnt, false);
+        //statusText.setBox(new Rectangle(0, 0, settings.getWidth(), settings.getHeight()));
+        statusText.setText("Running");
+        statusText.setLocalTranslation(0, settings.getHeight() - infoText.getHeight() - 2, 0);
+        guiNode.attachChild(statusText);
+    }
+
+    @Override
+    public void simpleUpdate(float tpf) {
+        //call kernel to test if it is still working
+        if (!failed) {
+            try {
+                kernel.Run1NoEvent(clQueue, new Kernel.WorkSize(1), buffer, 1.0f);
+            } catch (OpenCLException ex) {
+                LOG.log(Level.SEVERE, "Kernel call not working anymore", ex);
+                failed = true;
+                statusText.setText("Failed");
+            }
+        }
+    }
+}

+ 403 - 0
jme3-examples/src/main/java/jme3test/opencl/TestOpenCLLibraries.java

@@ -0,0 +1,403 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.opencl;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.font.BitmapFont;
+import com.jme3.font.BitmapText;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Matrix4f;
+import com.jme3.opencl.*;
+import com.jme3.system.AppSettings;
+import com.jme3.util.BufferUtils;
+import java.nio.*;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.Random;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Test class for the build in libraries
+ * @author shaman
+ */
+public class TestOpenCLLibraries extends SimpleApplication {
+    private static final Logger LOG = Logger.getLogger(TestOpenCLLibraries.class.getName());
+
+    public static void main(String[] args){
+        TestOpenCLLibraries app = new TestOpenCLLibraries();
+        AppSettings settings = new AppSettings(true);
+        settings.setOpenCLSupport(true);
+        settings.setVSync(true);
+//        settings.setRenderer(AppSettings.JOGL_OPENGL_FORWARD_COMPATIBLE);
+        app.setSettings(settings);
+        app.start(); // start the game
+    }
+
+    @Override
+    public void simpleInitApp() {
+        BitmapFont fnt = assetManager.loadFont("Interface/Fonts/Default.fnt");
+        Context clContext = context.getOpenCLContext();
+        if (clContext == null) {
+            BitmapText txt = new BitmapText(fnt);
+            txt.setText("No OpenCL Context created!\nSee output log for details.");
+            txt.setLocalTranslation(5, settings.getHeight() - 5, 0);
+            guiNode.attachChild(txt);
+            return;
+        }
+        CommandQueue clQueue = clContext.createQueue(clContext.getDevices().get(0));
+        
+        StringBuilder str = new StringBuilder();
+        str.append("OpenCL Context created:\n  Platform: ")
+                .append(clContext.getDevices().get(0).getPlatform().getName())
+                .append("\n  Devices: ").append(clContext.getDevices());
+        str.append("\nTests:");
+        str.append("\n  Random numbers: ").append(testRandom(clContext, clQueue));
+        str.append("\n  Matrix3f: ").append(testMatrix3f(clContext, clQueue));
+        str.append("\n  Matrix4f: ").append(testMatrix4f(clContext, clQueue));
+        
+        clQueue.release();
+        
+        BitmapText txt1 = new BitmapText(fnt);
+        txt1.setText(str.toString());
+        txt1.setLocalTranslation(5, settings.getHeight() - 5, 0);
+        guiNode.attachChild(txt1);
+        
+        flyCam.setEnabled(false);
+        inputManager.setCursorVisible(true);
+    }
+    
+    private static void assertEquals(byte expected, byte actual, String message) {
+        if (expected != actual) {
+            System.err.println(message+": expected="+expected+", actual="+actual);
+            throw new AssertionError();
+        }
+    }
+    private static void assertEquals(long expected, long actual, String message) {
+        if (expected != actual) {
+            System.err.println(message+": expected="+expected+", actual="+actual);
+            throw new AssertionError();
+        }
+    }
+    private static void assertEquals(double expected, double actual, String message) {
+        if (Math.abs(expected - actual) >= 0.00001) {
+            System.err.println(message+": expected="+expected+", actual="+actual);
+            throw new AssertionError();
+        }
+    }
+    private static void assertEquals(Object expected, Object actual, String message) {
+        if (!Objects.equals(expected, actual)) {
+            System.err.println(message+": expected="+expected+", actual="+actual);
+            throw new AssertionError();
+        }
+    }
+
+    private boolean testRandom(Context clContext, CommandQueue clQueue) {
+        try {
+            //test for doubles
+            boolean supportsDoubles = clContext.getDevices().get(0).hasDouble();
+            
+            //create code
+            String code = ""
+                    + "#import \"Common/OpenCL/Random.clh\"\n"
+                    + "__kernel void TestBool(__global ulong* seeds, __global uchar* results) {\n"
+                    + "  results[get_global_id(0)] = randBool(seeds + get_global_id(0)) ? 1 : 0;\n"
+                    + "}\n"
+                    + "__kernel void TestInt(__global ulong* seeds, __global int* results) {\n"
+                    + "  results[get_global_id(0)] = randInt(seeds + get_global_id(0));\n"
+                    + "}\n"
+                    + "__kernel void TestIntN(__global ulong* seeds, int n, __global int* results) {\n"
+                    + "  results[get_global_id(0)] = randIntN(n, seeds + get_global_id(0));\n"
+                    + "}\n"
+                    + "__kernel void TestLong(__global ulong* seeds, __global long* results) {\n"
+                    + "  results[get_global_id(0)] = randLong(seeds + get_global_id(0));\n"
+                    + "}\n"
+                    + "__kernel void TestFloat(__global ulong* seeds, __global float* results) {\n"
+                    + "  results[get_global_id(0)] = randFloat(seeds + get_global_id(0));\n"
+                    + "}\n"
+                    + "#ifdef RANDOM_DOUBLES\n"
+                    + "__kernel void TestDouble(__global ulong* seeds, __global double* results) {\n"
+                    + "  results[get_global_id(0)] = randDouble(seeds + get_global_id(0));\n"
+                    + "}\n"
+                    + "#endif\n";
+            if (supportsDoubles) {
+                code = "#define RANDOM_DOUBLES\n" + code;
+            }
+            Program program = clContext.createProgramFromSourceCodeWithDependencies(code, assetManager);
+            program.build();
+            
+            int count = 256;
+            Kernel.WorkSize ws = new Kernel.WorkSize(count);
+            
+            //create seeds
+            Random initRandom = new Random();
+            long[] seeds = new long[count];
+            Random[] randoms = new Random[count];
+            for (int i=0; i<count; ++i) {
+                seeds[i] = initRandom.nextLong();
+                randoms[i] = new Random(seeds[i]);
+                seeds[i] = (seeds[i] ^ 0x5DEECE66DL) & ((1L << 48) - 1); //needed because the Random constructor scrambles the initial seed
+            }
+            com.jme3.opencl.Buffer seedsBuffer = clContext.createBuffer(8 * count);
+            ByteBuffer tmpByteBuffer = BufferUtils.createByteBuffer(8 * count);
+            tmpByteBuffer.asLongBuffer().put(seeds);
+            seedsBuffer.write(clQueue, tmpByteBuffer);
+            
+            //test it
+            ByteBuffer resultByteBuffer = BufferUtils.createByteBuffer(8 * count);
+            IntBuffer resultIntBuffer = resultByteBuffer.asIntBuffer();
+            LongBuffer resultLongBuffer = resultByteBuffer.asLongBuffer();
+            FloatBuffer resultFloatBuffer = resultByteBuffer.asFloatBuffer();
+            DoubleBuffer resultDoubleBuffer = resultByteBuffer.asDoubleBuffer();
+            com.jme3.opencl.Buffer resultBuffer = clContext.createBuffer(8 * count);
+            //boolean
+            Kernel testBoolKernel = program.createKernel("TestBool");
+            testBoolKernel.Run1NoEvent(clQueue, ws, seedsBuffer, resultBuffer);
+            resultByteBuffer.rewind();
+            resultBuffer.read(clQueue, resultByteBuffer);
+            for (int i=0; i<count; ++i) {
+                assertEquals(randoms[i].nextBoolean() ? 1 : 0, resultByteBuffer.get(i), "randBool at i="+i);
+            }
+            testBoolKernel.release();
+            //int
+            Kernel testIntKernel = program.createKernel("TestInt");
+            testIntKernel.Run1NoEvent(clQueue, ws, seedsBuffer, resultBuffer);
+            resultByteBuffer.rewind();
+            resultBuffer.read(clQueue, resultByteBuffer);
+            for (int i=0; i<count; ++i) {
+                assertEquals(randoms[i].nextInt(), resultIntBuffer.get(i), "randInt at i="+i);
+            }
+            testIntKernel.release();
+            //int n
+            Kernel testIntNKernel = program.createKernel("TestIntN");
+            testIntNKernel.Run1NoEvent(clQueue, ws, seedsBuffer, 186, resultBuffer);
+            resultByteBuffer.rewind();
+            resultBuffer.read(clQueue, resultByteBuffer);
+            for (int i=0; i<count; ++i) {
+                assertEquals(randoms[i].nextInt(186), resultIntBuffer.get(i), "randInt at i="+i+" with n="+186);
+            }
+            testIntNKernel.Run1NoEvent(clQueue, ws, seedsBuffer, 97357, resultBuffer);
+            resultByteBuffer.rewind();
+            resultBuffer.read(clQueue, resultByteBuffer);
+            for (int i=0; i<count; ++i) {
+                assertEquals(randoms[i].nextInt(97357), resultIntBuffer.get(i), "randInt at i="+i+" with n="+97357);
+            }
+            testIntNKernel.release();
+            //long
+            Kernel testLongKernel = program.createKernel("TestLong");
+            testLongKernel.Run1NoEvent(clQueue, ws, seedsBuffer, resultBuffer);
+            resultByteBuffer.rewind();
+            resultBuffer.read(clQueue, resultByteBuffer);
+            for (int i=0; i<count; ++i) {
+                assertEquals(randoms[i].nextLong(), resultLongBuffer.get(i), "randLong at i="+i);
+            }
+            testLongKernel.release();
+            //float
+            Kernel testFloatKernel = program.createKernel("TestFloat");
+            testFloatKernel.Run1NoEvent(clQueue, ws, seedsBuffer, resultBuffer);
+            resultByteBuffer.rewind();
+            resultBuffer.read(clQueue, resultByteBuffer);
+            for (int i=0; i<count; ++i) {
+                assertEquals(randoms[i].nextFloat(), resultFloatBuffer.get(i), "randFloat at i="+i);
+            }
+            testFloatKernel.release();
+            //double
+            if (supportsDoubles) {
+                Kernel testDoubleKernel = program.createKernel("TestDouble");
+                testDoubleKernel.Run1NoEvent(clQueue, ws, seedsBuffer, resultBuffer);
+                resultByteBuffer.rewind();
+                resultBuffer.read(clQueue, resultByteBuffer);
+                for (int i=0; i<count; ++i) {
+                    assertEquals(randoms[i].nextDouble(), resultDoubleBuffer.get(i), "randLong at i="+i);
+                }
+                testDoubleKernel.release();
+            }
+            
+            seedsBuffer.release();
+            resultBuffer.release();
+            program.release();
+
+        } catch (AssertionError ex) {
+            LOG.log(Level.SEVERE, "random test failed with an assertion error");
+            return false;
+        } catch (Exception ex) {
+            LOG.log(Level.SEVERE, "random test failed with:", ex);
+            return false;
+        }
+        return true;
+    }
+    
+    private boolean testMatrix3f(Context clContext, CommandQueue clQueue) {
+        try {
+            
+            String code = ""
+                    + "#import \"Common/OpenCL/Matrix3f.clh\"\n"
+                    + "\n"
+                    + "__kernel void TestMatrix3f_1(__global char* result)\n"
+                    + "{\n"
+                    + "  mat3 id = mat3Identity();\n"
+                    + "  mat3 m1 = mat3FromRows( (float3)(23,-3,10.4f), (float3)(5,-8,2.22f), (float3)(-1,0,34) );\n"
+                    + "  mat3 m1Inv = mat3Invert(m1);\n"
+                    + "  mat3 m1Res = mat3Mult(m1, m1Inv);\n"
+                    + "  result[0] = mat3Equals(id, m1Res, 0.0001f) ? 1 : 0;\n"
+                    + "}\n"
+                    + "\n"
+                    + "__kernel void TestMatrix3f_2(mat3 m1, float a, mat3 m2, mat3 mRes, __global char* result)\n"
+                    + "{\n"
+                    + "  mat3 m = mat3Transpose(m1);\n"
+                    + "  m = mat3Add(mat3Scale(m, a), m2);\n"
+                    + "  result[0] = mat3Equals(mRes, m, 0.01f) ? 1 : 0;\n"
+                    + "}\n";
+            Program program = clContext.createProgramFromSourceCodeWithDependencies(code, assetManager);
+            program.build();
+            com.jme3.opencl.Buffer buffer = clContext.createBuffer(1);
+            
+            Kernel testMatrix3fKernel1 = program.createKernel("TestMatrix3f_1");
+            testMatrix3fKernel1.Run1NoEvent(clQueue, new Kernel.WorkSize(1), buffer);
+            ByteBuffer bb = buffer.map(clQueue, MappingAccess.MAP_READ_ONLY);
+            if (bb.get() == 0) {
+                LOG.severe("Matrix inversion failed");
+                return false;
+            }
+            buffer.unmap(clQueue, bb);
+            testMatrix3fKernel1.release();
+            
+            Kernel testMatrix3fKernel2 = program.createKernel("TestMatrix3f_2");
+            Matrix3f m1 = new Matrix3f(13.24f, -0.234f, 42, 83.23f, -34.2f, 3.2f, 0.25f, -42, 7.64f);
+            Matrix3f m2 = new Matrix3f(-5.2f, 0.757f, 2.01f, 12.0f, -6, 2, 0.01f, 9, 2.255f);
+            Matrix3f mRes = new Matrix3f(-31.68f, -165.703f, 1.51f, 12.468f, 62.4f, 86, -83.99f, 2.6f, -13.025f);
+            testMatrix3fKernel2.Run1NoEvent(clQueue, new Kernel.WorkSize(1), m1, -2.0f, m2, mRes, buffer);
+            bb = buffer.map(clQueue, MappingAccess.MAP_READ_ONLY);
+            if (bb.get() == 0) {
+                LOG.severe("Matrix add, multiply, transpose failed");
+                return false;
+            }
+            buffer.unmap(clQueue, bb);
+            testMatrix3fKernel2.release();
+            
+            buffer.release();
+            
+        } catch (AssertionError ex) {
+            LOG.log(Level.SEVERE, "matrix3f test failed with an assertion error");
+            return false;
+        } catch (Exception ex) {
+            LOG.log(Level.SEVERE, "matrix3f test failed with:", ex);
+            return false;
+        }
+        return true;
+    }
+    
+    private boolean testMatrix4f(Context clContext, CommandQueue clQueue) {
+        try {
+            
+            String code = ""
+                    + "#import \"Common/OpenCL/Matrix4f.clh\"\n"
+                    + "\n"
+                    + "__kernel void TestMatrix4f_1(mat4 m1, __global char* result)\n"
+                    + "{\n"
+                    + "  mat4 id = mat4Identity();\n"
+                    + "  mat4 m1Inv = mat4Invert(m1);\n"
+                    + "  mat4 m1Res = mat4Mult(m1, m1Inv);\n"
+                    + "  result[0] = mat4Equals(id, m1Res, 0.0001f) ? 1 : 0;\n"
+                    + "}\n"
+                    + "\n"
+                    + "__kernel void TestMatrix4f_2(mat4 m1, float d, mat4 m2, mat4 m3, __global char* result)\n"
+                    + "{\n"
+                    + "  float d2 = mat4Determinant(m1);\n"
+                    + "  result[0] = fabs(d - d2) < 0.0001f ? 1 : 0;\n"
+                    + "  mat4 res = mat4Transpose(m1);\n"
+                    + "  result[1] = mat4Equals(res, m2, 0.0001f) ? 1 : 0;\n"
+                    + "  res = mat4Adjoint(m1);\n"
+                    + "  result[2] = mat4Equals(res, m3, 0.0001f) ? 1 : 0;\n"
+                    + "}\n";
+            Program program = clContext.createProgramFromSourceCodeWithDependencies(code, assetManager);
+            program.build();
+            com.jme3.opencl.Buffer buffer = clContext.createBuffer(3);
+            
+            Random rand = new Random(1561);
+            
+            Kernel testMatrix4fKernel1 = program.createKernel("TestMatrix4f_1");
+            Matrix4f m1 = new Matrix4f();
+            do {
+                for (int i=0; i<4; ++i) {
+                    for (int j=0; j<4; ++j) {
+                        m1.set(i, j, rand.nextFloat()*20 - 10);
+                    }
+                }
+            } while (FastMath.abs(m1.determinant()) < 0.00001f);
+            testMatrix4fKernel1.Run1NoEvent(clQueue, new Kernel.WorkSize(1), m1, buffer);
+            ByteBuffer bb = buffer.map(clQueue, MappingAccess.MAP_READ_ONLY);
+            if (bb.get() == 0) {
+                LOG.severe("Matrix inversion failed");
+                return false;
+            }
+            buffer.unmap(clQueue, bb);
+            testMatrix4fKernel1.release();
+            
+            Kernel testMatrix4fKernel2 = program.createKernel("TestMatrix4f_2");
+            for (int i=0; i<4; ++i) {
+                for (int j=0; j<4; ++j) {
+                    m1.set(i, j, rand.nextFloat()*20 - 10);
+                }
+            }
+            testMatrix4fKernel2.Run1NoEvent(clQueue, new Kernel.WorkSize(1), m1, m1.determinant(), m1.transpose(), m1.adjoint(), buffer);
+            bb = buffer.map(clQueue, MappingAccess.MAP_READ_ONLY);
+            if (bb.get() == 0) {
+                LOG.severe("Matrix determinant computation failed");
+                return false;
+            }
+            if (bb.get() == 0) {
+                LOG.severe("Matrix transposing failed");
+                return false;
+            }
+            if (bb.get() == 0) {
+                LOG.severe("Matrix adjoint computation failed");
+                return false;
+            }
+            buffer.unmap(clQueue, bb);
+            testMatrix4fKernel2.release();
+            
+            buffer.release();
+            
+        } catch (AssertionError ex) {
+            LOG.log(Level.SEVERE, "matrix4f test failed with an assertion error");
+            return false;
+        } catch (Exception ex) {
+            LOG.log(Level.SEVERE, "matrix4f test failed with:", ex);
+            return false;
+        }
+        return true;
+    }
+}

+ 186 - 0
jme3-examples/src/main/java/jme3test/opencl/TestVertexBufferSharing.java

@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.opencl;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.opencl.*;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.shape.Box;
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeSystem;
+import com.jme3.util.BufferUtils;
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This test class tests the capability to read and modify an OpenGL vertex buffer.
+ * It is also shown how to use the program binaries to implement a simple program
+ * cache.
+ * @author shaman
+ */
+public class TestVertexBufferSharing extends SimpleApplication {
+    private static final Logger LOG = Logger.getLogger(TestVertexBufferSharing.class.getName());
+    
+    private int initCounter;
+    private Context clContext;
+    private CommandQueue clQueue;
+    private Geometry geom;
+    private Buffer buffer;
+    private Kernel kernel;
+    private com.jme3.opencl.Kernel.WorkSize ws;
+    private float time;
+
+    public static void main(String[] args){
+        TestVertexBufferSharing app = new TestVertexBufferSharing();
+        AppSettings settings = new AppSettings(true);
+        settings.setOpenCLSupport(true);
+        settings.setVSync(false);
+//        settings.setRenderer(AppSettings.JOGL_OPENGL_FORWARD_COMPATIBLE);
+        app.setSettings(settings);
+        app.start(); // start the game
+    }
+
+    @Override
+    public void simpleInitApp() {
+        initOpenCL1();
+        
+        Box b = new Box(1, 1, 1); // create cube shape
+        geom = new Geometry("Box", b);  // create cube geometry from the shape
+        Material mat = new Material(assetManager,
+          "Common/MatDefs/Misc/Unshaded.j3md");  // create a simple material
+        mat.setColor("Color", ColorRGBA.Blue);   // set color of material to blue
+        geom.setMaterial(mat);                   // set the cube's material
+        rootNode.attachChild(geom);              // make the cube appear in the scene
+        
+        initCounter = 0;
+        time = 0;
+        
+        flyCam.setDragToRotate(true);
+    }
+
+    @Override
+    public void simpleUpdate(float tpf) {
+        super.simpleUpdate(tpf);
+        
+        if (initCounter < 2) {
+        initCounter++;
+        } else if (initCounter == 2) {
+            //when initCounter reaches 2, the scene was drawn once and the texture was uploaded to the GPU
+            //then we can bind the texture to OpenCL
+            initOpenCL2();
+            updateOpenCL(tpf);
+            initCounter = 3;
+        } else {
+            updateOpenCL(tpf);
+        }
+    }
+    
+    private void initOpenCL1() {
+        clContext = context.getOpenCLContext();
+        Device device = clContext.getDevices().get(0);
+        clQueue = clContext.createQueue(device);
+        clQueue.register();
+        //create kernel
+        Program program = null;
+        File tmpFolder = JmeSystem.getStorageFolder();
+        File binaryFile = new File(tmpFolder, getClass().getSimpleName()+".clc");
+        try {
+            //attempt to load cached binary
+            byte[] bytes = Files.readAllBytes(binaryFile.toPath());
+            ByteBuffer bb = BufferUtils.createByteBuffer(bytes);
+            program = clContext.createProgramFromBinary(bb, device);
+            program.build();
+            LOG.info("reuse program from cached binaries");
+        } catch (java.nio.file.NoSuchFileException ex) {
+            //do nothing, cache was not created yet
+        } catch (Exception ex) {
+            LOG.log(Level.INFO, "Unable to use cached program binaries", ex);
+        }
+        if (program == null) {
+            //build from sources
+            String source = ""
+                    + "__kernel void ScaleKernel(__global float* vb, float scale)\n"
+                    + "{\n"
+                    + "  int idx = get_global_id(0);\n"
+                    + "  float3 pos = vload3(idx, vb);\n"
+                    + "  pos *= scale;\n"
+                    + "  vstore3(pos, idx, vb);\n"
+                    + "}\n";
+            program = clContext.createProgramFromSourceCode(source);
+            program.build();
+            //Save binary
+            try {
+                ByteBuffer bb = program.getBinary(device);
+                byte[] bytes = new byte[bb.remaining()];
+                bb.get(bytes);
+                Files.write(binaryFile.toPath(), bytes);
+            } catch (UnsupportedOperationException | OpenCLException | IOException ex) {
+               LOG.log(Level.SEVERE, "Unable to save program binaries", ex);
+            }
+            LOG.info("create new program from sources");
+        }
+        program.register();
+        kernel = program.createKernel("ScaleKernel");
+        kernel.register();
+    }
+    private void initOpenCL2() {
+        //bind vertex buffer to OpenCL
+        VertexBuffer vb = geom.getMesh().getBuffer(VertexBuffer.Type.Position);
+        buffer = clContext.bindVertexBuffer(vb, MemoryAccess.READ_WRITE);
+        buffer.register();
+        ws = new com.jme3.opencl.Kernel.WorkSize(geom.getMesh().getVertexCount());
+    }
+    private void updateOpenCL(float tpf) {
+        //advect time
+        time += tpf;
+        
+        //aquire resource
+        buffer.acquireBufferForSharingNoEvent(clQueue);
+        //no need to wait for the returned event, since the kernel implicitely waits for it (same command queue)
+        
+        //execute kernel
+        float scale = (float) Math.pow(1.1, (1.0 - time%2) / 16.0);
+        kernel.Run1NoEvent(clQueue, ws, buffer, scale);
+        
+        //release resource
+        buffer.releaseBufferForSharingNoEvent(clQueue);
+    }
+
+}

+ 183 - 0
jme3-examples/src/main/java/jme3test/opencl/TestWriteToTexture.java

@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.opencl;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.MouseInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.AnalogListener;
+import com.jme3.input.controls.MouseAxisTrigger;
+import com.jme3.input.controls.MouseButtonTrigger;
+import com.jme3.math.Vector2f;
+import com.jme3.opencl.*;
+import com.jme3.system.AppSettings;
+import com.jme3.texture.Texture2D;
+import com.jme3.ui.Picture;
+import java.util.logging.Logger;
+
+/**
+ * This test class tests the capability to write to a GL texture from OpenCL.
+ * Move the mouse around while pressing the left mouse key to modify the fractal.
+ * 
+ * In addition, this test shows how to use {@link ProgramCache}.
+ * 
+ * @author shaman
+ */
+public class TestWriteToTexture extends SimpleApplication implements AnalogListener, ActionListener {
+    private static final Logger LOG = Logger.getLogger(TestWriteToTexture.class.getName());
+    private static final float MOUSE_SPEED = 0.5f;
+    
+    private Texture2D tex;
+    private int initCounter;
+    private Context clContext;
+    private CommandQueue clQueue;
+    private ProgramCache programCache;
+    private Kernel kernel;
+    private Vector2f C;
+    private Image texCL;
+    private boolean dragging;
+
+    public static void main(String[] args){
+        TestWriteToTexture app = new TestWriteToTexture();
+        AppSettings settings = new AppSettings(true);
+        settings.setOpenCLSupport(true);
+        settings.setVSync(false);
+//        settings.setRenderer(AppSettings.JOGL_OPENGL_FORWARD_COMPATIBLE);
+        app.setSettings(settings);
+        app.start(); // start the game
+    }
+
+    @Override
+    public void simpleInitApp() {
+        initOpenCL1();
+        
+        tex = new Texture2D(settings.getWidth(), settings.getHeight(), 1, com.jme3.texture.Image.Format.RGBA8);
+        Picture pic = new Picture("julia");
+        pic.setTexture(assetManager, tex, true);
+        pic.setPosition(0, 0);
+        pic.setWidth(settings.getWidth());
+        pic.setHeight(settings.getHeight());
+        guiNode.attachChild(pic);
+        
+        initCounter = 0;
+        
+        flyCam.setEnabled(false);
+        inputManager.setCursorVisible(true);
+        inputManager.addMapping("right", new MouseAxisTrigger(MouseInput.AXIS_X, false));
+        inputManager.addMapping("left", new MouseAxisTrigger(MouseInput.AXIS_X, true));
+        inputManager.addMapping("up", new MouseAxisTrigger(MouseInput.AXIS_Y, false));
+        inputManager.addMapping("down", new MouseAxisTrigger(MouseInput.AXIS_Y, true));
+        inputManager.addMapping("drag", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
+        inputManager.addListener(this, "right", "left", "up", "down", "drag");
+        dragging = false;
+    }
+
+    @Override
+    public void simpleUpdate(float tpf) {
+        super.simpleUpdate(tpf);
+        
+        if (initCounter < 2) {
+        initCounter++;
+        } else if (initCounter == 2) {
+            //when initCounter reaches 2, the scene was drawn once and the texture was uploaded to the GPU
+            //then we can bind the texture to OpenCL
+            initOpenCL2();
+            updateOpenCL(tpf);
+            initCounter = 3;
+        } else {
+            updateOpenCL(tpf);
+        }
+        
+    }
+    
+    private void initOpenCL1() {
+        clContext = context.getOpenCLContext();
+        clQueue = clContext.createQueue();
+        clQueue.register();
+        programCache = new ProgramCache(clContext);
+        //create kernel
+        String cacheID = getClass().getName()+".Julia";
+        Program program = programCache.loadFromCache(cacheID);
+        if (program == null) {
+            LOG.info("Program not loaded from cache, create from sources instead");
+            program = clContext.createProgramFromSourceFiles(assetManager, "jme3test/opencl/JuliaSet.cl");
+            program.build();
+            programCache.saveToCache(cacheID, program);
+        }
+        program.register();
+        kernel = program.createKernel("JuliaSet");
+        kernel.register();
+        C = new Vector2f(0.12f, -0.2f);
+    }
+    private void initOpenCL2() {
+        //bind image to OpenCL
+        texCL = clContext.bindImage(tex, MemoryAccess.WRITE_ONLY);
+        texCL.register();
+    }
+    private void updateOpenCL(float tpf) {
+        //aquire resource
+        texCL.acquireImageForSharingNoEvent(clQueue);
+        //no need to wait for the returned event, since the kernel implicitely waits for it (same command queue)
+        
+        //execute kernel
+        Kernel.WorkSize ws = new Kernel.WorkSize(settings.getWidth(), settings.getHeight());
+        kernel.Run1NoEvent(clQueue, ws, texCL, C, 16);
+        
+        //release resource
+        texCL.releaseImageForSharingNoEvent(clQueue);
+    }
+
+    @Override
+    public void onAnalog(String name, float value, float tpf) {
+        if (!dragging) {
+            return;
+        }
+        if ("left".equals(name)) {
+            C.x -= tpf * MOUSE_SPEED;
+        } else if ("right".equals(name)) {
+            C.x += tpf * MOUSE_SPEED;
+        } else if ("up".equals(name)) {
+            C.y -= tpf * MOUSE_SPEED;
+        } else if ("down".equals(name)) {
+            C.y += tpf * MOUSE_SPEED;
+        }
+    }
+
+    @Override
+    public void onAction(String name, boolean isPressed, float tpf) {
+        if ("drag".equals(name)) {
+            dragging = isPressed;
+            inputManager.setCursorVisible(!isPressed);
+        }
+    }
+}

+ 4 - 0
jme3-examples/src/main/resources/jme3test/opencl/Blas.cl

@@ -0,0 +1,4 @@
+__kernel void Fill (__global TYPE* data, TYPE a)
+{
+	data[get_global_id(0)] = a;
+}

+ 27 - 0
jme3-examples/src/main/resources/jme3test/opencl/ContextSwitchingScreen.xml

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<nifty xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
+	   xmlns="http://nifty-gui.lessvoid.com/nifty-gui" 
+	   xsi:schemaLocation="https://raw.githubusercontent.com/void256/nifty-gui/1.4/nifty-core/src/main/resources/nifty.xsd https://raw.githubusercontent.com/void256/nifty-gui/1.4/nifty-core/src/main/resources/nifty.xsd">
+	
+	<useStyles filename="nifty-default-styles.xml" />
+	<useControls filename="nifty-default-controls.xml" />
+	
+	<screen id="Screen" controller="jme3test.opencl.TestContextSwitching">
+		<layer id="Layer0" childLayout="vertical">
+			<control name="label" id="InfoLabel" text="Current device:\n Platform: \n Device: \n Profile: \n Memory: \n Compute Units: " padding="10px" height="30%" />
+			<panel childLayout="horizontal" height="30%" >
+				<panel childLayout="vertical" width="50%" >
+					<control name="label" text="Select Platform" padding="10px"/>
+					<control id="PlatformListBox" name="listBox" vertical="optional" horizontal="optional" displayItems="4" selection="Single" padding="10px" />
+				</panel>
+				<panel childLayout="vertical" width="50%" >
+					<control name="label" text="Select Device" padding="10px"/>
+					<control id="DeviceListBox" name="listBox" vertical="optional" horizontal="optional" displayItems="4" selection="Single" padding="10px" />
+				</panel>
+			</panel>
+			<control name="button" id="ApplyButton" label="Change Context" align="center" valing="top" height="5%" padding="10px" />
+			<panel childLayout="center" height="30%" />
+		</layer>
+	</screen>
+	
+</nifty>

+ 99 - 0
jme3-examples/src/main/resources/jme3test/opencl/JuliaSet.cl

@@ -0,0 +1,99 @@
+
+
+//2 component vector to hold the real and imaginary parts of a complex number:
+typedef float2 cfloat;
+
+#define I ((cfloat)(0.0, 1.0))
+
+inline float real(cfloat a){
+     return a.x;
+}
+inline float imag(cfloat a){
+     return a.y;
+}
+
+inline float cmod(cfloat a){
+    return (sqrt(a.x*a.x + a.y*a.y));
+}
+
+inline cfloat cadd(cfloat a, cfloat b){
+	return (cfloat)( a.x + b.x, a.y + b.y);
+}
+
+inline float carg(cfloat a){
+    if(a.x > 0){
+        return atan(a.y / a.x);
+
+    }else if(a.x < 0 && a.y >= 0){
+        return atan(a.y / a.x) + M_PI_F;
+
+    }else if(a.x < 0 && a.y < 0){
+        return atan(a.y / a.x) - M_PI_F;
+
+    }else if(a.x == 0 && a.y > 0){
+        return M_PI_F/2;
+
+    }else if(a.x == 0 && a.y < 0){
+        return -M_PI_F/2;
+
+    }else{
+        return 0;
+    }
+}
+
+inline cfloat  cmult(cfloat a, cfloat b){
+    return (cfloat)( a.x*b.x - a.y*b.y, a.x*b.y + a.y*b.x);
+}
+
+inline cfloat csqrt(cfloat a){
+    return (cfloat)( sqrt(cmod(a)) * cos(carg(a)/2),  sqrt(cmod(a)) * sin(carg(a)/2));
+}
+
+inline float4 getColor(int iteration, int numIterations) {
+	//color transition: black -> red -> blue -> white
+	int step = numIterations / 2;
+	if (iteration < step) {
+		return mix( (float4)(0,0,0,1), (float4)(1,0,0,1), iteration / (float) step);
+	} else {
+		return mix( (float4)(1,0,0,1), (float4)(0,0,1,1), (iteration-step) / (float) (numIterations - step));
+	}
+}
+
+__kernel void JuliaSet(write_only image2d_t outputImage, const cfloat C, int numIterations)
+{
+	// get id of element in array
+	int x = get_global_id(0);
+	int y = get_global_id(1);
+	int w = get_global_size(0);
+	int h = get_global_size(1);
+
+	cfloat Z = { ( -w / 2 + x) / (w/4.0f) , ( -h / 2 + y) / (h/4.0f) };
+	int iteration = 0;
+
+	while (iteration < numIterations)
+	{
+		 cfloat Zpow2 = cmult(Z, Z); 
+		 cfloat Zn = cadd(Zpow2, C);
+		 Z.x = Zn.x;
+		 Z.y = Zn.y;
+		 iteration++;
+		 if(cmod(Z) > 2)
+		 {
+			break;
+		 }
+	}
+
+	float4 color;
+
+	// threshold reached mark pixel as white
+	if (iteration == numIterations)
+	{
+		color = (float4)(1,1,1,1);
+	}
+	else
+	{
+		color = getColor(iteration, numIterations);
+	}
+
+	write_imagef(outputImage, (int2)(x, y), color);
+}

+ 7 - 0
jme3-ios/src/main/java/com/jme3/system/ios/IGLESContext.java

@@ -36,6 +36,7 @@ import com.jme3.input.dummy.DummyKeyInput;
 import com.jme3.input.dummy.DummyMouseInput;
 import com.jme3.system.*;
 import com.jme3.input.ios.IosInputHandler;
+import com.jme3.opencl.Context;
 import com.jme3.renderer.ios.IosGL;
 import com.jme3.renderer.opengl.GL;
 import com.jme3.renderer.opengl.GLDebugES;
@@ -212,4 +213,10 @@ public class IGLESContext implements JmeContext {
             }
         }
     }
+
+    @Override
+    public Context getOpenCLContext() {
+        logger.warning("OpenCL not yet supported on this platform");
+        return null;
+    }
 }

+ 1 - 0
jme3-jogl/build.gradle

@@ -8,4 +8,5 @@ dependencies {
     compile 'org.jogamp.gluegen:gluegen-rt-main:2.3.2'
     compile 'org.jogamp.jogl:jogl-all-main:2.3.2'
     compile 'org.jogamp.joal:joal-main:2.3.2'
+	compile 'org.jogamp.jocl:jocl-main:2.3.2'
 }

+ 236 - 0
jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclBuffer.java

@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.jocl;
+
+import com.jme3.opencl.*;
+import java.nio.ByteBuffer;
+import com.jogamp.opencl.*;
+import com.jogamp.opencl.llb.CL;
+import com.jogamp.opencl.llb.gl.CLGL;
+import java.util.EnumSet;
+
+/**
+ *
+ * @author shaman
+ */
+public class JoclBuffer extends Buffer {
+
+    final long id;
+    final CL cl;
+
+    public JoclBuffer(long id) {
+        super(new ReleaserImpl(id));
+        this.id = id;
+        this.cl = CLPlatform.getLowLevelCLInterface();
+    }
+    
+    @Override
+    public long getSize() {
+        Utils.pointers[0].rewind();
+        int ret = cl.clGetMemObjectInfo(id, CL.CL_MEM_SIZE, Utils.pointers[0].elementSize(), Utils.pointers[0].getBuffer(), null);
+        Utils.checkError(ret, "clGetMemObjectInfo");
+        return Utils.pointers[0].get();
+    }
+
+    @Override
+    public MemoryAccess getMemoryAccessFlags() {
+        Utils.pointers[0].rewind();
+        int ret = cl.clGetMemObjectInfo(id, CL.CL_MEM_TYPE, Utils.pointers[0].elementSize(), Utils.pointers[0].getBuffer(), null);
+        Utils.checkError(ret, "clGetMemObjectInfo");
+        long flags = Utils.pointers[0].get();
+        return Utils.getMemoryAccessFromFlag(flags);
+    }
+
+    @Override
+    public void read(CommandQueue queue, ByteBuffer dest, long size, long offset) {
+        long q = ((JoclCommandQueue) queue).id;
+        int ret = cl.clEnqueueReadBuffer(q, id, CL.CL_TRUE, offset, size, dest, 0, null, null);
+        Utils.checkError(ret, "clEnqueueReadBuffer");
+    }
+
+    @Override
+    public Event readAsync(CommandQueue queue, ByteBuffer dest, long size, long offset) {
+        Utils.pointers[0].rewind();
+        long q = ((JoclCommandQueue) queue).id;
+        int ret = cl.clEnqueueReadBuffer(q, id, CL.CL_FALSE, offset, size, dest, 0, null, Utils.pointers[0]);
+        Utils.checkError(ret, "clEnqueueReadBuffer");
+        long event = Utils.pointers[0].get(0);
+        return new JoclEvent(event);
+    }
+
+    @Override
+    public void write(CommandQueue queue, ByteBuffer src, long size, long offset) {
+        long q = ((JoclCommandQueue)queue).id;
+        int ret = cl.clEnqueueWriteBuffer(q, id, CL.CL_TRUE, offset, size, src, 0, null, null);
+        Utils.checkError(ret, "clEnqueueWriteBuffer");
+    }
+
+    @Override
+    public Event writeAsync(CommandQueue queue, ByteBuffer src, long size, long offset) {
+        Utils.pointers[0].rewind();
+        long q = ((JoclCommandQueue)queue).id;
+        int ret = cl.clEnqueueWriteBuffer(q, id, CL.CL_FALSE, offset, size, src, 0, null, Utils.pointers[0]);
+        Utils.checkError(ret, "clEnqueueWriteBuffer");
+        long event = Utils.pointers[0].get(0);
+        return new JoclEvent(event);
+    }
+
+    @Override
+    public void copyTo(CommandQueue queue, Buffer dest, long size, long srcOffset, long destOffset) {
+        Utils.pointers[0].rewind();
+        long q = ((JoclCommandQueue)queue).id;
+        long did  = ((JoclBuffer) dest).id;
+        int ret = cl.clEnqueueCopyBuffer(q, id, did, srcOffset, destOffset, size, 0, null, Utils.pointers[0]);
+        Utils.checkError(ret, "clEnqueueCopyBuffer");
+        ret = cl.clWaitForEvents(1, Utils.pointers[0]);
+        Utils.checkError(ret, "clWaitForEvents");
+    }
+
+    @Override
+    public Event copyToAsync(CommandQueue queue, Buffer dest, long size, long srcOffset, long destOffset) {
+        Utils.pointers[0].rewind();
+        long q = ((JoclCommandQueue)queue).id;
+        long did  = ((JoclBuffer) dest).id;
+        int ret = cl.clEnqueueCopyBuffer(q, id, did, srcOffset, destOffset, size, 0, null, Utils.pointers[0]);
+        Utils.checkError(ret, "clEnqueueCopyBuffer");
+        long event = Utils.pointers[0].get(0);
+        return new JoclEvent(event);
+    }
+
+    @Override
+    public ByteBuffer map(CommandQueue queue, long size, long offset, MappingAccess access) {
+        long q = ((JoclCommandQueue)queue).id;
+        Utils.errorBuffer.rewind();
+        long flags = Utils.getMappingAccessFlags(access);
+        ByteBuffer b = cl.clEnqueueMapBuffer(q, id, CL.CL_TRUE, flags, offset, size, 0, null, null, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clEnqueueMapBuffer");
+        return b;
+    }
+
+    @Override
+    public void unmap(CommandQueue queue, ByteBuffer ptr) {
+        long q = ((JoclCommandQueue)queue).id;
+        Utils.pointers[0].rewind();
+        ptr.position(0);
+        int ret = cl.clEnqueueUnmapMemObject(q, id, ptr, 0, null, Utils.pointers[0]);
+        Utils.checkError(ret, "clEnqueueUnmapMemObject");
+        ret = cl.clWaitForEvents(1, Utils.pointers[0]);
+        Utils.checkError(ret, "clWaitForEvents");
+    }
+
+    @Override
+    public com.jme3.opencl.Buffer.AsyncMapping mapAsync(CommandQueue queue, long size, long offset, MappingAccess access) {
+        long q = ((JoclCommandQueue)queue).id;
+        Utils.pointers[0].rewind();
+        Utils.errorBuffer.rewind();
+        long flags = Utils.getMappingAccessFlags(access);
+        ByteBuffer b = cl.clEnqueueMapBuffer(q, id, CL.CL_FALSE, flags, offset, size, 0, null, Utils.pointers[0], Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clEnqueueMapBuffer");
+        long event = Utils.pointers[0].get(0);
+        return new com.jme3.opencl.Buffer.AsyncMapping(new JoclEvent(event), b);
+    }
+
+    @Override
+    public Event fillAsync(CommandQueue queue, ByteBuffer pattern, long size, long offset) {
+        throw new UnsupportedOperationException("Not supported by Jocl!");
+    }
+
+    @Override
+    public Event copyToImageAsync(CommandQueue queue, Image dest, long srcOffset, long[] destOrigin, long[] destRegion) {
+        if (destOrigin.length!=3 || destRegion.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointers[0].rewind();
+        Utils.pointers[1].rewind();
+        Utils.pointers[2].rewind();
+        Utils.pointers[1].put(destOrigin[0]).put(destOrigin[1]).put(destOrigin[2]).position(0);
+        Utils.pointers[2].put(destRegion[0]).put(destRegion[1]).put(destRegion[2]).position(0);
+        long q = ((JoclCommandQueue)queue).id;
+        long i = ((JoclImage) dest).id;
+        int ret = cl.clEnqueueCopyBufferToImage(q, id, i, srcOffset, Utils.pointers[1], Utils.pointers[2], 0, null, Utils.pointers[0]);
+        Utils.checkError(ret, "clEnqueueCopyBufferToImage");
+        long event = Utils.pointers[0].get(0);
+        return new JoclEvent(event);
+    }
+
+    @Override
+    public Event acquireBufferForSharingAsync(CommandQueue queue) {
+        Utils.pointers[0].rewind();
+        Utils.pointers[1].rewind();
+        Utils.pointers[1].put(0, id);
+        long q = ((JoclCommandQueue)queue).id;
+        ((CLGL) cl).clEnqueueAcquireGLObjects(q, 1, Utils.pointers[1], 0, null, Utils.pointers[0]);
+        long event = Utils.pointers[0].get(0);
+        return new JoclEvent(event);
+    }
+    @Override
+    public void acquireBufferForSharingNoEvent(CommandQueue queue) {
+        Utils.pointers[1].rewind();
+        Utils.pointers[1].put(0, id);
+        long q = ((JoclCommandQueue)queue).id;
+        ((CLGL) cl).clEnqueueAcquireGLObjects(q, 1, Utils.pointers[1], 0, null, null);
+    }
+
+    @Override
+    public Event releaseBufferForSharingAsync(CommandQueue queue) {
+        Utils.pointers[0].rewind();
+        Utils.pointers[1].rewind();
+        Utils.pointers[1].put(0, id);
+        long q = ((JoclCommandQueue)queue).id;
+        ((CLGL) cl).clEnqueueReleaseGLObjects(q, 1, Utils.pointers[1], 0, null, Utils.pointers[0]);
+        long event = Utils.pointers[0].get(0);
+        return new JoclEvent(event);
+    }
+    @Override
+    public void releaseBufferForSharingNoEvent(CommandQueue queue) {
+        Utils.pointers[1].rewind();
+        Utils.pointers[1].put(0, id);
+        long q = ((JoclCommandQueue)queue).id;
+        ((CLGL) cl).clEnqueueReleaseGLObjects(q, 1, Utils.pointers[1], 0, null, null);
+    }
+
+    private static class ReleaserImpl implements ObjectReleaser {
+        private long mem;
+        private ReleaserImpl(long mem) {
+            this.mem = mem;
+        }
+        @Override
+        public void release() {
+            if (mem != 0) {
+                int ret = CLPlatform.getLowLevelCLInterface().clReleaseMemObject(mem);
+                mem = 0;
+                Utils.reportError(ret, "clReleaseMemObject");
+            }
+        }
+        
+    }
+}

+ 84 - 0
jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclCommandQueue.java

@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.jocl;
+
+import com.jme3.opencl.CommandQueue;
+import com.jme3.opencl.OpenCLObjectManager;
+import com.jogamp.opencl.CLCommandQueue;
+import com.jogamp.opencl.CLPlatform;
+import com.jogamp.opencl.llb.CL;
+import com.jogamp.opencl.llb.CLCommandQueueBinding;
+
+/**
+ *
+ * @author shaman
+ */
+public class JoclCommandQueue extends CommandQueue {
+
+    final CL cl;
+    final long id;
+
+    public JoclCommandQueue(long id) {
+        super(new ReleaserImpl(id));
+        this.id = id;
+        this.cl = CLPlatform.getLowLevelCLInterface();
+    }
+    
+    @Override
+    public void flush() {  
+        int ret = cl.clFlush(id);
+        Utils.checkError(ret, "clFlush");
+    }
+
+    @Override
+    public void finish() {
+        int ret = cl.clFinish(id);
+        Utils.checkError(ret, "clFinish");
+    }
+    
+    private static class ReleaserImpl implements ObjectReleaser {
+        private long id;
+
+        private ReleaserImpl(long id) {
+            this.id = id;
+        }
+        
+        @Override
+        public void release() {
+            if (id != 0) {
+                int ret = CLPlatform.getLowLevelCLInterface().clReleaseCommandQueue(id);
+                id = 0;
+                Utils.reportError(ret, "clReleaseCommandQueue");
+            }
+        }
+    }
+}

+ 262 - 0
jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclContext.java

@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.jocl;
+
+import com.jme3.opencl.*;
+import com.jme3.opencl.Context;
+import com.jme3.opencl.Image.ImageDescriptor;
+import com.jme3.opencl.Image.ImageFormat;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Texture;
+import com.jogamp.opencl.CLContext;
+import com.jogamp.opencl.CLImageFormat;
+import com.jogamp.opencl.CLMemory.Mem;
+import com.jogamp.opencl.CLPlatform;
+import com.jogamp.opencl.llb.CL;
+import com.jogamp.opencl.llb.gl.CLGL;
+import com.jogamp.opencl.llb.impl.CLImageFormatImpl;
+import com.jogamp.opengl.GL;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author shaman
+ */
+public class JoclContext extends Context {
+    private static final Logger LOG = Logger.getLogger(JoclContext.class.getName());
+    
+    final CLContext context;
+    final long id;
+    final CL cl; 
+    private final List<JoclDevice> devices;
+
+    public JoclContext(CLContext context, List<JoclDevice> devices) {
+        super(new ReleaserImpl(context.ID, devices));
+        this.context = context;
+        this.id = context.ID;
+        this.cl = context.getCL();
+        this.devices = devices;
+    }
+
+    public CLContext getContext() {
+        return context;
+    }
+
+    @Override
+    public List<JoclDevice> getDevices() {
+        return devices;
+    }
+
+    @Override
+    @SuppressWarnings("element-type-mismatch")
+    public CommandQueue createQueue(Device device) {
+        assert (devices.contains(device)); //this also ensures that device is a JoclDevice
+        long d = ((JoclDevice) device).id;
+        long properties = 0;
+        long q = cl.clCreateCommandQueue(id, d, properties, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateCommandQueue");
+        return new JoclCommandQueue(q);
+    }
+    
+    @Override
+    public Buffer createBuffer(long size, MemoryAccess access) {
+        long flags = Utils.getMemoryAccessFlags(access);
+        long mem = cl.clCreateBuffer(id, flags, size, null, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateBuffer");
+        return new JoclBuffer(mem);
+    }
+
+    @Override
+    public Buffer createBufferFromHost(ByteBuffer data, MemoryAccess access) {
+        long flags = Utils.getMemoryAccessFlags(access);
+        flags |= CL.CL_MEM_USE_HOST_PTR;
+        long mem = cl.clCreateBuffer(id, flags, data.capacity(), data, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateBuffer");
+        return new JoclBuffer(mem);
+    }
+
+    @Override
+    public Image createImage(MemoryAccess access, ImageFormat format, ImageDescriptor descr) {
+        if (descr.type != Image.ImageType.IMAGE_2D && descr.type != Image.ImageType.IMAGE_3D) {
+            throw new UnsupportedOperationException("Jocl only supports 2D and 3D images");
+        }
+        long memFlags = Utils.getMemoryAccessFlags(access);
+        Utils.errorBuffer.rewind();
+        //fill image format
+        CLImageFormatImpl f = CLImageFormatImpl.create();
+        f.setImageChannelOrder(JoclImage.decodeImageChannelOrder(format.channelOrder));
+        f.setImageChannelDataType(JoclImage.decodeImageChannelType(format.channelType));
+        //create image
+        long mem;
+        if (descr.type == Image.ImageType.IMAGE_2D) {
+            mem = cl.clCreateImage2D(id, memFlags, f, descr.width, descr.height, 
+                    descr.hostPtr==null ? 0 : descr.rowPitch, descr.hostPtr, Utils.errorBuffer);
+            Utils.checkError(Utils.errorBuffer, "clCreateImage2D");
+        } else {
+            mem = cl.clCreateImage3D(id, memFlags, f, descr.width, descr.height, descr.depth, 
+                    descr.hostPtr==null ? 0 : descr.rowPitch, descr.hostPtr==null ? 0 : descr.slicePitch, 
+                    descr.hostPtr, Utils.errorBuffer);
+            Utils.checkError(Utils.errorBuffer, "clCreateImage3D");
+        }
+        return new JoclImage(mem);
+    }
+
+    @Override
+    public ImageFormat[] querySupportedFormats(MemoryAccess access, Image.ImageType type) {
+        if (type != Image.ImageType.IMAGE_2D && type != Image.ImageType.IMAGE_3D) {
+            throw new UnsupportedOperationException("Jocl only supports 2D and 3D images");
+        }
+        long memFlags = Utils.getMemoryAccessFlags(access);
+        CLImageFormat[] fx;
+        if (type == Image.ImageType.IMAGE_2D) {
+            fx = context.getSupportedImage2dFormats(Mem.valueOf((int) memFlags));
+        }  else {
+            fx = context.getSupportedImage3dFormats(Mem.valueOf((int) memFlags));
+        }
+        //convert formats
+        ImageFormat[] formats = new ImageFormat[fx.length];
+        for (int i=0; i<fx.length; ++i) {
+            Image.ImageChannelOrder channelOrder = JoclImage.encodeImageChannelOrder(fx[i].getFormatImpl().getImageChannelOrder());
+            Image.ImageChannelType channelType = JoclImage.encodeImageChannelType(fx[i].getFormatImpl().getImageChannelDataType());
+            formats[i] = new ImageFormat(channelOrder, channelType);
+        }
+        return formats;
+    }
+
+    @Override
+    public Buffer bindVertexBuffer(VertexBuffer vb, MemoryAccess access) {
+        int vbId = vb.getId();
+        if (vbId == -1) {
+            throw new IllegalArgumentException("vertex buffer was not yet uploaded to the GPU or is CPU only");
+        }
+        long flags = Utils.getMemoryAccessFlags(access);
+        Utils.errorBuffer.rewind();
+        long mem = ((CLGL) cl).clCreateFromGLBuffer(id, flags, vbId, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateFromGLBuffer");
+        return new JoclBuffer(mem);
+    }
+
+    @Override
+    public Image bindImage(com.jme3.texture.Image image, Texture.Type textureType, int miplevel, MemoryAccess access) {
+        int imageID = image.getId();
+        if (imageID == -1) {
+            throw new IllegalArgumentException("image was not yet uploaded to the GPU");
+        }
+        long memFlags = Utils.getMemoryAccessFlags(access);
+        int textureTarget = convertTextureType(textureType);
+        Utils.errorBuffer.rewind();
+        long mem;
+        if (textureType == Texture.Type.TwoDimensional) {
+            mem = ((CLGL) cl).clCreateFromGLTexture2D(id, memFlags, textureTarget, miplevel, imageID, Utils.errorBuffer);
+        } else if (textureType == Texture.Type.ThreeDimensional) {
+            mem = ((CLGL) cl).clCreateFromGLTexture3D(id, memFlags, textureTarget, miplevel, imageID, Utils.errorBuffer);
+        } else {
+            throw new UnsupportedOperationException("Jocl only supports 2D and 3D images");
+        }
+        Utils.checkError(Utils.errorBuffer, "clCreateFromGLTexture");
+        return new JoclImage(mem);
+    }
+
+    @Override
+    protected Image bindPureRenderBuffer(FrameBuffer.RenderBuffer buffer, MemoryAccess access) {
+        int renderbuffer = buffer.getId();
+        if (renderbuffer == -1) {
+            throw new IllegalArgumentException("renderbuffer was not yet uploaded to the GPU");
+        }
+        long memFlags = Utils.getMemoryAccessFlags(access);
+        Utils.errorBuffer.rewind();
+        long mem = ((CLGL) cl).clCreateFromGLRenderbuffer(id, memFlags, renderbuffer, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateFromGLRenderbuffer");
+        return new JoclImage(mem);
+    }
+    
+    private int convertTextureType(Texture.Type textureType) {
+        switch (textureType) {
+            case TwoDimensional: return GL.GL_TEXTURE_2D;
+            case CubeMap: return GL.GL_TEXTURE_CUBE_MAP;
+            default: throw new IllegalArgumentException("unknown or unsupported texture type "+textureType);
+        }
+    }
+
+    @Override
+    public Program createProgramFromSourceCode(String sourceCode) {
+        LOG.log(Level.FINE, "Create program from source:\n{0}", sourceCode);
+        Utils.errorBuffer.rewind();
+        Utils.pointers[0].rewind();
+        Utils.pointers[0].put(0, sourceCode.length());
+        long p = cl.clCreateProgramWithSource(id, 1, new String[]{sourceCode}, Utils.pointers[0], Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateProgramWithSource");
+        return new JoclProgram(p, this);
+    }
+
+    @Override
+    public Program createProgramFromBinary(ByteBuffer binaries, Device device) {
+        binaries.rewind();
+        Utils.errorBuffer.rewind();
+        Utils.tempBuffers[0].b16i.rewind();
+        Utils.pointers[0].rewind();
+        Utils.pointers[0].put(0, ((JoclDevice) device).id);
+        Utils.pointers[1].rewind();
+        Utils.pointers[1].put(0, binaries.remaining());
+        Utils.pointers[2].rewind();
+        Utils.pointers[2].referenceBuffer(0, binaries);
+        long p = cl.clCreateProgramWithBinary(id, 1, Utils.pointers[0], 
+                Utils.pointers[1], Utils.pointers[2], Utils.tempBuffers[0].b16i, 
+                Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateProgramWithBinary");
+        Utils.checkError(Utils.tempBuffers[0].b16i, "clCreateProgramWithBinary");
+        return new JoclProgram(p, this);
+    }
+
+    private static class ReleaserImpl implements ObjectReleaser {
+        private long id;
+        private final List<JoclDevice> devices;
+        private ReleaserImpl(long id, List<JoclDevice> devices) {
+            this.id = id;
+            this.devices = devices;
+        }
+        @Override
+        public void release() {
+            if (id != 0) {
+                int ret = CLPlatform.getLowLevelCLInterface().clReleaseContext(id);
+                id = 0;
+                devices.clear();
+                Utils.reportError(ret, "clReleaseContext");
+            }
+        }
+        
+    }
+}

+ 302 - 0
jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclDevice.java

@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.jocl;
+
+import com.jme3.opencl.Device;
+import com.jme3.opencl.Platform;
+import com.jogamp.opencl.CLDevice;
+import java.util.Arrays;
+import java.util.Collection;
+
+/**
+ *
+ * @author shaman
+ */
+public final class JoclDevice implements Device {
+
+    final long id;
+    final CLDevice device;
+    final JoclPlatform platform;
+
+    public JoclDevice(CLDevice device, JoclPlatform platform) {
+        this.id = device.ID;
+        this.device = device;
+        this.platform = platform;
+    }
+
+    public long getId() {
+        return id;
+    }
+
+    public CLDevice getDevice() {
+        return device;
+    }
+    
+    @Override
+    public JoclPlatform getPlatform() {
+        return platform;
+    }
+
+    @Override
+    public DeviceType getDeviceType() {
+        CLDevice.Type type = device.getType();
+        switch (type) {
+            case ACCELERATOR: return DeviceType.ACCELEARTOR;
+            case CPU: return DeviceType.CPU;
+            case GPU: return DeviceType.GPU;
+            default: return DeviceType.DEFAULT;
+        }
+    }
+
+    @Override
+    public int getVendorId() {
+        return (int) device.getVendorID();
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return device.isAvailable();
+    }
+
+    @Override
+    public boolean hasCompiler() {
+        return device.isCompilerAvailable();
+    }
+
+    @Override
+    public boolean hasDouble() {
+        return hasExtension("cl_khr_fp64");
+    }
+
+    @Override
+    public boolean hasHalfFloat() {
+        return hasExtension("cl_khr_fp16");
+    }
+
+    @Override
+    public boolean hasErrorCorrectingMemory() {
+        return device.isErrorCorrectionSupported();
+    }
+
+    @Override
+    public boolean hasUnifiedMemory() {
+        return device.isMemoryUnified();
+    }
+
+    @Override
+    public boolean hasImageSupport() {
+        return device.isImageSupportAvailable();
+    }
+    
+    @Override
+    public boolean hasWritableImage3D() {
+        return hasExtension("cl_khr_3d_image_writes");
+    }
+
+    @Override
+    public boolean hasOpenGLInterop() {
+        return hasExtension("cl_khr_gl_sharing");
+    }
+    
+    @Override
+    public boolean hasExtension(String extension) {
+        return getExtensions().contains(extension);
+    }
+
+    @Override
+    public Collection<? extends String> getExtensions() {
+        return device.getExtensions();
+    }
+
+    @Override
+    public int getComputeUnits() {
+        return device.getMaxComputeUnits();
+    }
+
+    @Override
+    public int getClockFrequency() {
+        return device.getMaxClockFrequency();
+    }
+
+    @Override
+    public int getAddressBits() {
+        return device.getAddressBits();
+    }
+
+    @Override
+    public boolean isLittleEndian() {
+        return device.isLittleEndian();
+    }
+
+    @Override
+    public long getMaximumWorkItemDimensions() {
+        return device.getMaxWorkItemDimensions();
+    }
+
+    @Override
+    public long[] getMaximumWorkItemSizes() {
+        int[] sizes = device.getMaxWorkItemSizes();
+        long[] s = new long[sizes.length];
+        for (int i=0; i<sizes.length; ++i) {
+            s[i] = sizes[i];
+        }
+        return s;
+    }
+
+    @Override
+    public long getMaxiumWorkItemsPerGroup() {
+        return device.getMaxWorkGroupSize();
+    }
+
+    @Override
+    public int getMaximumSamplers() {
+        return device.getMaxSamplers();
+    }
+
+    @Override
+    public int getMaximumReadImages() {
+        return device.getMaxReadImageArgs();
+    }
+
+    @Override
+    public int getMaximumWriteImages() {
+        return device.getMaxWriteImageArgs();
+    }
+
+    @Override
+    public long[] getMaximumImage2DSize() {
+        return new long[] {
+            device.getMaxImage2dWidth(),
+            device.getMaxImage2dHeight()
+        };
+    }
+
+    @Override
+    public long[] getMaximumImage3DSize() {
+        return new long[] {
+            device.getMaxImage3dWidth(),
+            device.getMaxImage3dHeight(),
+            device.getMaxImage3dDepth()
+        };
+    }
+    
+    @Override
+    public long getMaximumAllocationSize() {
+        return device.getMaxMemAllocSize();
+    }
+    
+    @Override
+    public long getGlobalMemorySize() {
+        return device.getGlobalMemSize();
+    }
+    
+    @Override
+    public long getLocalMemorySize() {
+        return device.getLocalMemSize();
+    }
+    
+    @Override
+    public long getMaximumConstantBufferSize() {
+        return device.getMaxConstantBufferSize();
+    }
+    
+    @Override
+    public int getMaximumConstantArguments() {
+        return (int) device.getMaxConstantArgs();
+    }
+
+    @Override
+    public String getProfile() {
+        return device.getProfile();
+    }
+
+    @Override
+    public String getVersion() {
+        return device.getVersion().toString();
+    }
+
+    @Override
+    public int getVersionMajor() {
+        return Utils.getMajorVersion(getVersion(), "OpenCL ");
+    }
+
+    @Override
+    public int getVersionMinor() {
+        return Utils.getMinorVersion(getVersion(), "OpenCL ");
+    }
+
+    @Override
+    public String getCompilerVersion() {
+        return "OpenCL C 1.1"; //at most OpenCL 1.1 is supported at all
+    }
+
+    @Override
+    public int getCompilerVersionMajor() {
+        return Utils.getMajorVersion(getCompilerVersion(), "OpenCL C ");
+    }
+
+    @Override
+    public int getCompilerVersionMinor() {
+        return Utils.getMinorVersion(getCompilerVersion(), "OpenCL C ");
+    }
+
+    @Override
+    public String getDriverVersion() {
+        return device.getDriverVersion();
+    }
+
+    @Override
+    public int getDriverVersionMajor() {
+        return Utils.getMajorVersion(getDriverVersion(), "");
+    }
+
+    @Override
+    public int getDriverVersionMinor() {
+        return Utils.getMinorVersion(getDriverVersion(), "");
+    }
+
+    @Override
+    public String getName() {
+        return device.getName();
+    }
+
+    @Override
+    public String getVendor() {
+        return device.getVendor();
+    }
+
+    @Override
+    public String toString() {
+        return getName();
+    }
+    
+}

+ 100 - 0
jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclEvent.java

@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.jocl;
+
+import com.jme3.opencl.Event;
+import com.jme3.opencl.OpenCLObjectManager;
+import com.jogamp.opencl.CLPlatform;
+import com.jogamp.opencl.llb.CL;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author shaman
+ */
+public class JoclEvent extends Event {
+    private static final Logger LOG = Logger.getLogger(JoclEvent.class.getName());
+    
+    final long id;
+    final CL cl;
+
+    public JoclEvent(long id) {
+        super(new ReleaserImpl(id));
+        this.id = id;
+        this.cl = CLPlatform.getLowLevelCLInterface();
+    }
+
+    @Override
+    public void waitForFinished() {
+        Utils.pointers[0].rewind();
+        Utils.pointers[0].put(0, id);
+        int ret = cl.clWaitForEvents(1, Utils.pointers[0]);
+        Utils.checkError(ret, "clWaitForEvents");
+        release();
+    }
+
+    @Override
+    public boolean isCompleted() {
+        Utils.tempBuffers[0].b16.rewind();
+        int err = cl.clGetEventInfo(id, CL.CL_EVENT_COMMAND_EXECUTION_STATUS, 4, Utils.tempBuffers[0].b16, null);
+        Utils.checkError(err, "clGetEventInfo");
+        int status = Utils.tempBuffers[0].b16i.get(0);
+        if (status == CL.CL_SUCCESS) {
+            release();
+            return true;
+        } else if (status < 0) {
+            Utils.checkError(status, "EventStatus");
+            return false;
+        } else {
+            return false;
+        }
+    }
+    
+    private static class ReleaserImpl implements ObjectReleaser {
+        private long event;
+
+        private ReleaserImpl(long event) {
+            this.event = event;
+        }
+        
+        @Override
+        public void release() {
+            if (event != 0) {
+                int ret = CLPlatform.getLowLevelCLInterface().clReleaseEvent(event);
+                event = 0;
+                Utils.reportError(ret, "clReleaseEvent");
+                LOG.finer("Event deleted");
+            }
+        }
+        
+    }
+}

+ 544 - 0
jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclImage.java

@@ -0,0 +1,544 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.jocl;
+
+import com.jme3.math.ColorRGBA;
+import com.jme3.opencl.*;
+import com.jogamp.opencl.CLPlatform;
+import com.jogamp.opencl.llb.CL;
+import com.jogamp.opencl.llb.gl.CLGL;
+import java.nio.ByteBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author shaman
+ */
+public class JoclImage extends Image {
+    private static final Logger LOG = Logger.getLogger(JoclImage.class.getName());
+
+    final long id;
+    final CL cl;
+
+    public JoclImage(long image) {
+        super(new ReleaserImpl(image));
+        this.id = image;
+        this.cl = CLPlatform.getLowLevelCLInterface();
+    }
+
+    public static int decodeImageChannelOrder(ImageChannelOrder order) {
+        switch (order) {
+            case A:
+                return CL.CL_A;
+            case ARGB:
+                return CL.CL_ARGB;
+            case BGRA:
+                return CL.CL_BGRA;
+            case INTENSITY:
+                return CL.CL_INTENSITY;
+            case LUMINANCE:
+                return CL.CL_LUMINANCE;
+            case R:
+                return CL.CL_R;
+            case RA:
+                return CL.CL_RA;
+            case RG:
+                return CL.CL_RG;
+            case RGB:
+                return CL.CL_RGB;
+            case RGBA:
+                return CL.CL_RGBA;
+            case RGBx:
+                return CL.CL_RGBx;
+            case RGx:
+                return CL.CL_RGx;
+            case Rx:
+                return CL.CL_Rx;
+            default:
+                throw new IllegalArgumentException("unknown image channel order: " + order);
+        }
+    }
+
+    public static ImageChannelOrder encodeImageChannelOrder(int order) {
+        switch (order) {
+            case CL.CL_A:
+                return ImageChannelOrder.A;
+            case CL.CL_ARGB:
+                return ImageChannelOrder.ARGB;
+            case CL.CL_BGRA:
+                return ImageChannelOrder.BGRA;
+            case CL.CL_INTENSITY:
+                return ImageChannelOrder.INTENSITY;
+            case CL.CL_LUMINANCE:
+                return ImageChannelOrder.LUMINANCE;
+            case CL.CL_R:
+                return ImageChannelOrder.R;
+            case CL.CL_RA:
+                return ImageChannelOrder.RA;
+            case CL.CL_RG:
+                return ImageChannelOrder.RG;
+            case CL.CL_RGB:
+                return ImageChannelOrder.RGB;
+            case CL.CL_RGBA:
+                return ImageChannelOrder.RGBA;
+            case CL.CL_RGBx:
+                return ImageChannelOrder.RGBx;
+            case CL.CL_RGx:
+                return ImageChannelOrder.RGx;
+            case CL.CL_Rx:
+                return ImageChannelOrder.Rx;
+            default:
+                //throw new com.jme3.opencl.OpenCLException("unknown image channel order id: " + order);
+                LOG.log(Level.WARNING, "Unknown image channel order id: {0}", order);
+                return null;
+        }
+    }
+
+    public static int decodeImageChannelType(ImageChannelType type) {
+        switch (type) {
+            case FLOAT:
+                return CL.CL_FLOAT;
+            case HALF_FLOAT:
+                return CL.CL_HALF_FLOAT;
+            case SIGNED_INT16:
+                return CL.CL_SIGNED_INT16;
+            case SIGNED_INT32:
+                return CL.CL_SIGNED_INT32;
+            case SIGNED_INT8:
+                return CL.CL_SIGNED_INT8;
+            case SNORM_INT16:
+                return CL.CL_SNORM_INT16;
+            case SNORM_INT8:
+                return CL.CL_SNORM_INT8;
+            case UNORM_INT8:
+                return CL.CL_UNORM_INT8;
+            case UNORM_INT_101010:
+                return CL.CL_UNORM_INT_101010;
+            case UNORM_INT16:
+                return CL.CL_UNORM_INT16;
+            case UNORM_SHORT_565:
+                return CL.CL_UNORM_SHORT_565;
+            case UNORM_SHORT_555:
+                return CL.CL_UNORM_SHORT_555;
+            case UNSIGNED_INT16:
+                return CL.CL_UNSIGNED_INT16;
+            case UNSIGNED_INT32:
+                return CL.CL_UNSIGNED_INT32;
+            case UNSIGNED_INT8:
+                return CL.CL_UNSIGNED_INT8;
+            default:
+                throw new IllegalArgumentException("Unknown image channel type: " + type);
+        }
+    }
+
+    public static ImageChannelType encodeImageChannelType(int type) {
+        switch (type) {
+            case CL.CL_FLOAT:
+                return ImageChannelType.FLOAT;
+            case CL.CL_HALF_FLOAT:
+                return ImageChannelType.HALF_FLOAT;
+            case CL.CL_SIGNED_INT16:
+                return ImageChannelType.SIGNED_INT16;
+            case CL.CL_SIGNED_INT32:
+                return ImageChannelType.SIGNED_INT32;
+            case CL.CL_SIGNED_INT8:
+                return ImageChannelType.SIGNED_INT8;
+            case CL.CL_SNORM_INT16:
+                return ImageChannelType.SNORM_INT16;
+            case CL.CL_SNORM_INT8:
+                return ImageChannelType.SNORM_INT8;
+            case CL.CL_UNORM_INT8:
+                return ImageChannelType.UNORM_INT8;
+            case CL.CL_UNORM_INT16:
+                return ImageChannelType.UNORM_INT16;
+            case CL.CL_UNORM_INT_101010:
+                return ImageChannelType.UNORM_INT_101010;
+            case CL.CL_UNORM_SHORT_555:
+                return ImageChannelType.UNORM_SHORT_555;
+            case CL.CL_UNORM_SHORT_565:
+                return ImageChannelType.UNORM_SHORT_565;
+            case CL.CL_UNSIGNED_INT16:
+                return ImageChannelType.UNSIGNED_INT16;
+            case CL.CL_UNSIGNED_INT32:
+                return ImageChannelType.UNSIGNED_INT32;
+            case CL.CL_UNSIGNED_INT8:
+                return ImageChannelType.UNSIGNED_INT8;
+            default:
+                //throw new com.jme3.opencl.OpenCLException("unknown image channel type id: " + type);
+                LOG.log(Level.WARNING, "Unknown image channel type id: {0}", type);
+                return null;
+        }
+    }
+
+    public static int decodeImageType(ImageType type) {
+        switch (type) {
+//            case IMAGE_1D:
+//                return CL.CL_MEM_OBJECT_IMAGE1D;
+//            case IMAGE_1D_ARRAY:
+//                return CL.CL_MEM_OBJECT_IMAGE1D_ARRAY;
+//            case IMAGE_1D_BUFFER:
+//                return CL.CL_MEM_OBJECT_IMAGE1D_BUFFER;
+            case IMAGE_2D:
+                return CL.CL_MEM_OBJECT_IMAGE2D;
+//            case IMAGE_2D_ARRAY:
+//                return CL.CL_MEM_OBJECT_IMAGE2D_ARRAY;
+            case IMAGE_3D:
+                return CL.CL_MEM_OBJECT_IMAGE3D;
+            default:
+                throw new IllegalArgumentException("Unknown or unsupported image type: " + type);
+        }
+    }
+
+    public static ImageType encodeImageType(int type) {
+        switch (type) {
+//            case CL12.CL_MEM_OBJECT_IMAGE1D:
+//                return ImageType.IMAGE_1D;
+//            case CL12.CL_MEM_OBJECT_IMAGE1D_ARRAY:
+//                return ImageType.IMAGE_1D_ARRAY;
+//            case CL12.CL_MEM_OBJECT_IMAGE1D_BUFFER:
+//                return ImageType.IMAGE_1D_BUFFER;
+            case CL.CL_MEM_OBJECT_IMAGE2D:
+                return ImageType.IMAGE_2D;
+//            case CL12.CL_MEM_OBJECT_IMAGE2D_ARRAY:
+//                return ImageType.IMAGE_2D_ARRAY;
+            case CL.CL_MEM_OBJECT_IMAGE3D:
+                return ImageType.IMAGE_3D;
+            default:
+                //throw new com.jme3.opencl.OpenCLException("Unknown image type id: " + type);
+                LOG.log(Level.WARNING, "Unknown or unsupported image type with id: {0}", type);
+                return null;
+        }
+    }
+
+    private long getInfoSize(int param) {
+        Utils.tempBuffers[0].b16l.rewind();
+        int ret = cl.clGetImageInfo(id, param, 8, Utils.tempBuffers[0].b16l, null);
+        Utils.checkError(ret, "clGetImageInfo");
+        return Utils.tempBuffers[0].b16l.get(0);
+    }
+    
+    @Override
+    public long getWidth() {
+        return getInfoSize(CL.CL_IMAGE_WIDTH);
+    }
+
+    @Override
+    public long getHeight() {
+        return getInfoSize(CL.CL_IMAGE_HEIGHT);
+    }
+
+    @Override
+    public long getDepth() {
+        return getInfoSize(CL.CL_IMAGE_DEPTH);
+    }
+
+    @Override
+    public long getRowPitch() {
+        return getInfoSize(CL.CL_IMAGE_ROW_PITCH);
+    }
+
+    @Override
+    public long getSlicePitch() {
+        return getInfoSize(CL.CL_IMAGE_SLICE_PITCH);
+    }
+
+    @Override
+    public long getArraySize() {
+        //return getInfoSize(CL12.CL_IMAGE_ARRAY_SIZE);
+        throw new UnsupportedOperationException("Not supported in Jocl");
+    }
+
+    @Override
+    public ImageFormat getImageFormat() {
+        Utils.tempBuffers[0].b16i.rewind();
+        int ret = cl.clGetImageInfo(id, CL.CL_IMAGE_FORMAT, 8, Utils.tempBuffers[0].b16i, null);
+        Utils.checkError(ret, "clGetImageInfo");
+        int channelOrder = Utils.tempBuffers[0].b16i.get(0);
+        int channelType = Utils.tempBuffers[0].b16i.get(1);
+        return new ImageFormat(encodeImageChannelOrder(channelOrder), encodeImageChannelType(channelType));
+    }
+
+    @Override
+    public ImageType getImageType() {
+        Utils.tempBuffers[0].b16i.rewind();
+        int ret = cl.clGetMemObjectInfo(id, CL.CL_IMAGE_FORMAT, 5, Utils.tempBuffers[0].b16i, null);
+        int type = Utils.tempBuffers[0].b16i.get(0);
+        return encodeImageType(type);
+    }
+
+    @Override
+    public int getElementSize() {
+        return (int) getInfoSize(CL.CL_IMAGE_ELEMENT_SIZE);
+    }
+
+    @Override
+    public void readImage(CommandQueue queue, ByteBuffer dest, long[] origin, long[] region, long rowPitch, long slicePitch) {
+        if (origin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointers[1].rewind();
+        Utils.pointers[2].rewind();
+        Utils.pointers[1].put(origin, 0, 3).position(0);
+        Utils.pointers[2].put(region, 0, 3).position(0);
+        long q = ((JoclCommandQueue) queue).id;
+        int ret = cl.clEnqueueReadImage(q, id, CL.CL_TRUE, Utils.pointers[1], Utils.pointers[2], rowPitch, slicePitch, dest, 0, null, null);
+        Utils.checkError(ret, "clEnqueueReadImage");
+    }
+
+    @Override
+    public Event readImageAsync(CommandQueue queue, ByteBuffer dest, long[] origin, long[] region, long rowPitch, long slicePitch) {
+        if (origin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointers[0].rewind();
+        Utils.pointers[1].rewind();
+        Utils.pointers[2].rewind();
+        Utils.pointers[1].put(origin, 0, 3).position(0);
+        Utils.pointers[2].put(region, 0, 3).position(0);
+        long q = ((JoclCommandQueue) queue).id;
+        int ret = cl.clEnqueueReadImage(q, id, CL.CL_FALSE, Utils.pointers[1], Utils.pointers[2], rowPitch, slicePitch, dest, 0, null, Utils.pointers[0]);
+        Utils.checkError(ret, "clEnqueueReadImage");
+        long event = Utils.pointers[0].get(0);
+        return new JoclEvent(event);
+    }
+
+    @Override
+    public void writeImage(CommandQueue queue, ByteBuffer dest, long[] origin, long[] region, long rowPitch, long slicePitch) {
+        if (origin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointers[1].rewind();
+        Utils.pointers[2].rewind();
+        Utils.pointers[1].put(origin, 0, 3).position(0);
+        Utils.pointers[2].put(region, 0, 3).position(0);
+        long q = ((JoclCommandQueue) queue).id;
+        int ret = cl.clEnqueueWriteImage(q, id, CL.CL_TRUE, Utils.pointers[1], Utils.pointers[2], rowPitch, slicePitch, dest, 0, null, null);
+        Utils.checkError(ret, "clEnqueueWriteImage");
+    }
+
+    @Override
+    public Event writeImageAsync(CommandQueue queue, ByteBuffer dest, long[] origin, long[] region, long rowPitch, long slicePitch) {
+        if (origin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointers[0].rewind();
+        Utils.pointers[1].rewind();
+        Utils.pointers[2].rewind();
+        Utils.pointers[1].put(origin, 0, 3).position(0);
+        Utils.pointers[2].put(region, 0, 3).position(0);
+        long q = ((JoclCommandQueue) queue).id;
+        int ret = cl.clEnqueueWriteImage(q, id, CL.CL_FALSE, Utils.pointers[1], Utils.pointers[2], rowPitch, slicePitch, dest, 0, null, Utils.pointers[0]);
+        Utils.checkError(ret, "clEnqueueWriteImage");
+        long event = Utils.pointers[0].get(0);
+        return new JoclEvent(event);
+    }
+
+    @Override
+    public void copyTo(CommandQueue queue, Image dest, long[] srcOrigin, long[] destOrigin, long[] region) {
+        if (srcOrigin.length!=3 || destOrigin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointers[0].rewind();
+        Utils.pointers[1].rewind();
+        Utils.pointers[2].rewind();
+        Utils.pointers[3].rewind();
+        Utils.pointers[1].put(srcOrigin, 0, 3).position(0);
+        Utils.pointers[2].put(destOrigin, 0, 3).position(0);
+        Utils.pointers[3].put(region, 0, 3).position(0);
+        long q = ((JoclCommandQueue) queue).id;
+        int ret = cl.clEnqueueCopyImage(q, id, ((JoclImage) dest).id, Utils.pointers[1], Utils.pointers[2], Utils.pointers[3], 0, null, Utils.pointers[0]);
+        Utils.checkError(ret, "clEnqueueCopyImage");
+        ret = cl.clWaitForEvents(1, Utils.pointers[0]);
+        Utils.checkError(ret, "clWaitForEvents");
+    }
+
+    @Override
+    public Event copyToAsync(CommandQueue queue, Image dest, long[] srcOrigin, long[] destOrigin, long[] region) {
+        if (srcOrigin.length!=3 || destOrigin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointers[0].rewind();
+        Utils.pointers[1].rewind();
+        Utils.pointers[2].rewind();
+        Utils.pointers[3].rewind();
+        Utils.pointers[1].put(srcOrigin, 0, 3).position(0);
+        Utils.pointers[2].put(destOrigin, 0, 3).position(0);
+        Utils.pointers[3].put(region, 0, 3).position(0);
+        long q = ((JoclCommandQueue) queue).id;
+        int ret = cl.clEnqueueCopyImage(q, id, ((JoclImage) dest).id, Utils.pointers[1], Utils.pointers[2], Utils.pointers[3], 0, null, Utils.pointers[0]);
+        Utils.checkError(ret, "clEnqueueCopyImage");
+        long event = Utils.pointers[0].get(0);
+        return new JoclEvent(event);
+    }
+
+    @Override
+    public ImageMapping map(CommandQueue queue, long[] origin, long[] region, MappingAccess access) {
+        if (origin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.errorBuffer.rewind();
+        Utils.pointers[1].rewind();
+        Utils.pointers[2].rewind();
+        Utils.pointers[3].rewind();
+        Utils.pointers[4].rewind();
+        Utils.pointers[1].put(origin, 0, 3).position(0);
+        Utils.pointers[2].put(region, 0, 3).position(0);
+        long q = ((JoclCommandQueue) queue).id;
+        long flags = Utils.getMappingAccessFlags(access);
+        ByteBuffer buf = cl.clEnqueueMapImage(q, id, CL.CL_TRUE, flags, Utils.pointers[1], Utils.pointers[2], 
+                Utils.pointers[3], Utils.pointers[4], 0, null, null, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clEnqueueMapBuffer");
+        return new ImageMapping(buf, Utils.pointers[3].get(0), Utils.pointers[4].get(0));
+    }
+
+    @Override
+    public ImageMapping mapAsync(CommandQueue queue, long[] origin, long[] region, MappingAccess access) {
+        if (origin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.errorBuffer.rewind();
+        Utils.pointers[0].rewind();
+        Utils.pointers[1].rewind();
+        Utils.pointers[2].rewind();
+        Utils.pointers[3].rewind();
+        Utils.pointers[4].rewind();
+        Utils.pointers[1].put(origin, 0, 3).position(0);
+        Utils.pointers[2].put(region, 0, 3).position(0);
+        long q = ((JoclCommandQueue) queue).id;
+        long flags = Utils.getMappingAccessFlags(access);
+        ByteBuffer buf = cl.clEnqueueMapImage(q, id, CL.CL_FALSE, flags, Utils.pointers[1], Utils.pointers[2], 
+                Utils.pointers[3], Utils.pointers[4], 0, null, Utils.pointers[0], Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clEnqueueMapBuffer");
+        long event = Utils.pointers[0].get(0);
+        return new ImageMapping(buf, Utils.pointers[3].get(0), Utils.pointers[4].get(0), 
+                new JoclEvent(event));
+    }
+
+    @Override
+    public void unmap(CommandQueue queue, ImageMapping mapping) {
+        long q = ((JoclCommandQueue)queue).id;
+        Utils.pointers[0].rewind();
+        mapping.buffer.position(0);
+        int ret = cl.clEnqueueUnmapMemObject(q, id, mapping.buffer, 0, null, Utils.pointers[0]);
+        Utils.checkError(ret, "clEnqueueUnmapMemObject");
+        ret = cl.clWaitForEvents(1, Utils.pointers[0]);
+        Utils.checkError(ret, "clWaitForEvents");
+    }
+
+    @Override
+    public Event fillAsync(CommandQueue queue, long[] origin, long[] region, ColorRGBA color) {
+        if (origin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        throw new UnsupportedOperationException("Not supported by Jocl!");
+    }
+
+    @Override
+    public Event fillAsync(CommandQueue queue, long[] origin, long[] region, int[] color) {
+        if (color.length != 4) {
+            throw new IllegalArgumentException("the passed color array must have length 4");
+        }
+        if (origin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        throw new UnsupportedOperationException("Not supported by Jocl!");
+    }
+
+    @Override
+    public Event copyToBufferAsync(CommandQueue queue, Buffer dest, long[] srcOrigin, long[] srcRegion, long destOffset) {
+        if (srcOrigin.length!=3 || srcRegion.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointers[0].rewind();
+        Utils.pointers[1].rewind();
+        Utils.pointers[2].rewind();
+        Utils.pointers[1].put(srcOrigin, 0, 3).position(0);
+        Utils.pointers[2].put(srcRegion, 0, 3).position(0);
+        long q = ((JoclCommandQueue) queue).id;
+        int ret = cl.clEnqueueCopyImageToBuffer(q, id, ((JoclBuffer) dest).id, 
+                Utils.pointers[1], Utils.pointers[2], destOffset, 0, null, Utils.pointers[0]);
+        Utils.checkError(ret, "clEnqueueCopyImageToBuffer");
+        long event = Utils.pointers[0].get(0);
+        return new JoclEvent(event);
+    }
+
+    @Override
+    public Event acquireImageForSharingAsync(CommandQueue queue) {
+        Utils.pointers[0].rewind();
+        Utils.pointers[1].rewind();
+        Utils.pointers[1].put(0, id);
+        long q = ((JoclCommandQueue)queue).id;
+        ((CLGL) cl).clEnqueueAcquireGLObjects(q, 1, Utils.pointers[1], 0, null, Utils.pointers[0]);
+        long event = Utils.pointers[0].get(0);
+        return new JoclEvent(event);
+    }
+    @Override
+    public void acquireImageForSharingNoEvent(CommandQueue queue) {
+        Utils.pointers[1].rewind();
+        Utils.pointers[1].put(0, id);
+        long q = ((JoclCommandQueue)queue).id;
+        ((CLGL) cl).clEnqueueAcquireGLObjects(q, 1, Utils.pointers[1], 0, null, null);
+    }
+    @Override
+    public Event releaseImageForSharingAsync(CommandQueue queue) {
+        Utils.pointers[0].rewind();
+        Utils.pointers[1].rewind();
+        Utils.pointers[1].put(0, id);
+        long q = ((JoclCommandQueue)queue).id;
+        ((CLGL) cl).clEnqueueReleaseGLObjects(q, 1, Utils.pointers[1], 0, null, Utils.pointers[0]);
+        long event = Utils.pointers[0].get(0);
+        return new JoclEvent(event);
+    }
+    @Override
+    public void releaseImageForSharingNoEvent(CommandQueue queue) {
+        Utils.pointers[1].rewind();
+        Utils.pointers[1].put(0, id);
+        long q = ((JoclCommandQueue)queue).id;
+        ((CLGL) cl).clEnqueueReleaseGLObjects(q, 1, Utils.pointers[1], 0, null, null);
+    }
+    
+    private static class ReleaserImpl implements ObjectReleaser {
+        private long mem;
+        private ReleaserImpl(long mem) {
+            this.mem = mem;
+        }
+        @Override
+        public void release() {
+            if (mem != 0) {
+                int ret = CLPlatform.getLowLevelCLInterface().clReleaseMemObject(mem);
+                mem = 0;
+                Utils.reportError(ret, "clReleaseMemObject");
+            }
+        }
+    }
+}

+ 290 - 0
jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclKernel.java

@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.jocl;
+
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector4f;
+import com.jme3.opencl.*;
+import com.jme3.opencl.Buffer;
+import com.jogamp.common.nio.PointerBuffer;
+import com.jogamp.opencl.CLPlatform;
+import com.jogamp.opencl.llb.CL;
+import java.nio.*;
+import java.nio.charset.Charset;
+
+import static com.jogamp.common.os.Platform.is32Bit;
+
+/**
+ *
+ * @author shaman
+ */
+public class JoclKernel extends Kernel {
+
+    final long kernel;
+    final CL cl;
+
+    public JoclKernel(long kernel) {
+        super(new ReleaserImpl(kernel));
+        this.kernel = kernel;
+        this.cl = CLPlatform.getLowLevelCLInterface();
+        OpenCLObjectManager.getInstance().registerObject(this);
+    }
+    
+    @Override
+    public String getName() {
+        Utils.pointers[0].rewind();
+        int ret = cl.clGetKernelInfo(kernel, CL.CL_KERNEL_FUNCTION_NAME, 0, null, Utils.pointers[0]);
+        Utils.checkError(ret, "clGetKernelInfo");
+        int count = (int) Utils.pointers[0].get(0);
+        ByteBuffer buf = ByteBuffer.allocateDirect(count);
+        ret = cl.clGetKernelInfo(kernel, CL.CL_KERNEL_FUNCTION_NAME, count, buf, null);
+        Utils.checkError(ret, "clGetKernelInfo");
+        byte[] data = new byte[count];
+        buf.get(data);
+        return new String(data, Charset.forName("ASCII"));
+    }
+
+    @Override
+    public int getArgCount() {
+        Utils.tempBuffers[0].b16i.rewind();
+        int ret = cl.clGetKernelInfo(kernel, CL.CL_KERNEL_NUM_ARGS, 4, Utils.tempBuffers[0].b16i, null);
+        Utils.checkError(ret, "clGetKernelInfo");
+        return Utils.tempBuffers[0].b16i.get(0);
+    }
+
+    @Override
+    public long getMaxWorkGroupSize(Device device) {
+        long d = ((JoclDevice) device).id;
+        Utils.tempBuffers[0].b16l.rewind();
+        int ret = cl.clGetKernelWorkGroupInfo(kernel, d, CL.CL_KERNEL_WORK_GROUP_SIZE, 8, Utils.tempBuffers[0].b16l, null);
+        Utils.checkError(ret, "clGetKernelWorkGroupInfo");
+        return Utils.tempBuffers[0].b16l.get(0);
+   }
+    
+    @Override
+    public void setArg(int index, LocalMemPerElement t) {
+        int ret = cl.clSetKernelArg (kernel, index, t.getSize() * workGroupSize.getSizes()[0] * workGroupSize.getSizes()[1] * workGroupSize.getSizes()[2], null);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, LocalMem t) {
+        int ret = cl.clSetKernelArg (kernel, index, t.getSize(), null);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, Buffer t) {
+        Utils.tempBuffers[0].b16l.rewind();
+        Utils.tempBuffers[0].b16l.put(0, ((JoclBuffer) t).id);
+        int ret = cl.clSetKernelArg(kernel, index, is32Bit()?4:8, Utils.tempBuffers[0].b16l);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+    
+    @Override
+    public void setArg(int index, Image i) {
+        Utils.tempBuffers[0].b16l.rewind();
+        Utils.tempBuffers[0].b16l.put(0, ((JoclImage) i).id);
+        int ret = cl.clSetKernelArg(kernel, index, is32Bit()?4:8, Utils.tempBuffers[0].b16l);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, byte b) {
+        ByteBuffer buf = Utils.tempBuffers[0].b16;
+        buf.position(0);
+        buf.put(0, b);
+        int ret = cl.clSetKernelArg(kernel, index, 1, buf);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, short s) {
+        ShortBuffer buf = Utils.tempBuffers[0].b16s;
+        buf.position(0);
+        buf.put(0, s);
+        int ret = cl.clSetKernelArg(kernel, index, 2, buf);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, int i) {
+        IntBuffer buf = Utils.tempBuffers[0].b16i;
+        buf.position(0);
+        buf.limit(1);
+        buf.put(0, i);
+        int ret = cl.clSetKernelArg(kernel, index, 4, buf);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, long l) {
+        LongBuffer buf = Utils.tempBuffers[0].b16l;
+        buf.position(0);
+        buf.limit(1);
+        buf.put(0, l);
+        int ret = cl.clSetKernelArg(kernel, index, 8, buf);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, float f) {
+        FloatBuffer buf = Utils.tempBuffers[0].b16f;
+        buf.position(0);
+        buf.limit(1);
+        buf.put(0, f);
+        int ret = cl.clSetKernelArg(kernel, index, 4, buf);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, double d) {
+        DoubleBuffer buf = Utils.tempBuffers[0].b16d;
+        buf.position(0);
+        buf.limit(1);
+        buf.put(0, d);
+        int ret = cl.clSetKernelArg(kernel, index, 8, buf);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, Vector2f v) {
+        FloatBuffer buf = Utils.tempBuffers[0].b16f;
+        buf.position(0);
+        buf.limit(2);
+        buf.put(0, v.x);
+        buf.put(1, v.y);
+        int ret = cl.clSetKernelArg(kernel, index, 8, buf);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, Vector4f v) {
+        FloatBuffer buf = Utils.tempBuffers[0].b16f;
+        buf.position(0);
+        buf.limit(4);
+        buf.put(0, v.x);
+        buf.put(1, v.y);
+        buf.put(2, v.z);
+        buf.put(3, v.w);
+        int ret = cl.clSetKernelArg(kernel, index, 16, buf);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, Quaternion q) {
+        FloatBuffer buf = Utils.tempBuffers[0].b16f;
+        buf.position(0);
+        buf.limit(4);
+        buf.put(0, q.getX());
+        buf.put(1, q.getY());
+        buf.put(2, q.getZ());
+        buf.put(3, q.getW());
+        int ret = cl.clSetKernelArg(kernel, index, 16, buf);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+    
+    @Override
+    public void setArg(int index, Matrix4f m) {
+        FloatBuffer buf = Utils.b80f;
+        buf.position(0);
+        buf.limit(16);
+        buf.put(m.m00).put(m.m01).put(m.m02).put(m.m03);
+        buf.put(m.m10).put(m.m11).put(m.m12).put(m.m13);
+        buf.put(m.m20).put(m.m21).put(m.m22).put(m.m23);
+        buf.put(m.m30).put(m.m31).put(m.m32).put(m.m33);
+        buf.position(0);
+        int ret = cl.clSetKernelArg(kernel, index, 16*4, buf);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, ByteBuffer buffer, long size) {
+        int ret = cl.clSetKernelArg(kernel, index, size, buffer);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public Event Run(CommandQueue queue) {
+        Utils.pointers[0].rewind();
+        Utils.pointers[1].rewind();
+        Utils.pointers[1].put(globalWorkSize.getSizes(), 0, globalWorkSize.getSizes().length);
+        Utils.pointers[1].position(0);
+        PointerBuffer p2 = null;
+        if (workGroupSize.getSizes()[0] > 0) {
+            p2 = Utils.pointers[2].rewind();
+            p2.put(workGroupSize.getSizes(), 0, workGroupSize.getSizes().length);
+            p2.position(0);
+        }
+        long q = ((JoclCommandQueue) queue).id;
+        int ret = cl.clEnqueueNDRangeKernel(q, kernel,
+			globalWorkSize.getDimension(), null, Utils.pointers[1],
+			p2, 0, null, Utils.pointers[0]);
+        Utils.checkError(ret, "clEnqueueNDRangeKernel");
+        return new JoclEvent(Utils.pointers[0].get(0));
+    }
+    
+    @Override
+    public void RunNoEvent(CommandQueue queue) {
+        Utils.pointers[1].rewind();
+        Utils.pointers[1].put(globalWorkSize.getSizes(), 0, globalWorkSize.getSizes().length);
+        Utils.pointers[1].position(0);
+        PointerBuffer p2 = null;
+        if (workGroupSize.getSizes()[0] > 0) {
+            p2 = Utils.pointers[2].rewind();
+            p2.put(workGroupSize.getSizes(), 0, workGroupSize.getSizes().length);
+            p2.position(0);
+        }
+        long q = ((JoclCommandQueue) queue).id;
+        int ret = cl.clEnqueueNDRangeKernel(q, kernel,
+			globalWorkSize.getDimension(), null, Utils.pointers[1],
+			p2, 0, null, null);
+        Utils.checkError(ret, "clEnqueueNDRangeKernel");
+    }
+
+    private static class ReleaserImpl implements ObjectReleaser {
+        private long kernel;
+        private ReleaserImpl(long kernel) {
+            this.kernel = kernel;
+        }
+        @Override
+        public void release() {
+            if (kernel != 0) {
+                int ret = CLPlatform.getLowLevelCLInterface().clReleaseKernel(kernel);
+                kernel = 0;
+                Utils.reportError(ret, "clReleaseKernel");
+            }
+        }
+    }
+}

+ 127 - 0
jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclPlatform.java

@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.jocl;
+
+import com.jme3.opencl.Device;
+import com.jme3.opencl.Platform;
+import com.jogamp.opencl.CLDevice;
+import com.jogamp.opencl.CLPlatform;
+import com.jogamp.opencl.llb.CL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ *
+ * @author shaman
+ */
+public final class JoclPlatform implements Platform {
+    
+    final CLPlatform platform;
+    List<JoclDevice> devices;
+    
+    public JoclPlatform(CLPlatform platform) {
+        this.platform = platform;
+    }
+
+    public CLPlatform getPlatform() {
+        return platform;
+    }
+    
+    @Override
+    public List<JoclDevice> getDevices() {
+        if (devices == null) {
+            devices = new ArrayList<>();
+            for (CLDevice d : platform.listCLDevices()) {
+                devices.add(new JoclDevice(d, this));
+            }
+        }
+        return devices;
+    }
+
+    @Override
+    public String getProfile() {
+        return platform.getProfile();
+    }
+
+    @Override
+    public boolean isFullProfile() {
+        return getProfile().contains("FULL_PROFILE");
+    }
+
+    @Override
+    public boolean isEmbeddedProfile() {
+        return getProfile().contains("EMBEDDED_PROFILE");
+    }
+
+    @Override
+    public String getVersion() {
+        return platform.getVendor();
+    }
+
+    @Override
+    public int getVersionMajor() {
+        return Utils.getMajorVersion(getVersion(), "OpenCL ");
+    }
+
+    @Override
+    public int getVersionMinor() {
+        return Utils.getMinorVersion(getVersion(), "OpenCL ");
+    }
+
+    @Override
+    public String getName() {
+        return platform.getName();
+    }
+
+    @Override
+    public String getVendor() {
+        return platform.getVendor();
+    }
+
+    @Override
+    public boolean hasExtension(String extension) {
+        return getExtensions().contains(extension);
+    }
+
+    @Override
+    public boolean hasOpenGLInterop() {
+        return hasExtension("cl_khr_gl_sharing");
+    }
+
+    @Override
+    public Collection<? extends String> getExtensions() {
+        return platform.getExtensions();
+    }
+
+}

+ 194 - 0
jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclProgram.java

@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.jocl;
+
+import com.jme3.opencl.*;
+import com.jogamp.common.nio.Buffers;
+import com.jogamp.common.nio.PointerBuffer;
+import com.jogamp.opencl.CLPlatform;
+import com.jogamp.opencl.CLProgram;
+import com.jogamp.opencl.llb.CL;
+import com.jogamp.opencl.util.CLUtil;
+import java.nio.ByteBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static com.jogamp.common.nio.Buffers.newDirectByteBuffer;
+/**
+ *
+ * @author shaman
+ */
+public class JoclProgram extends Program {
+    private static final Logger LOG = Logger.getLogger(JoclProgram.class.getName());
+    
+    final long program;
+    final CL cl;
+    private final JoclContext context;
+
+    public JoclProgram(long program, JoclContext context) {
+        super(new ReleaserImpl(program));
+        this.program = program;
+        this.context = context;
+        this.cl = CLPlatform.getLowLevelCLInterface();
+    }
+
+    @Override
+    public void build(String args, Device... devices) throws KernelCompilationException {
+        PointerBuffer deviceList = null;
+        int deviceCount = 0;
+        if (devices != null) {
+            deviceList = PointerBuffer.allocateDirect(devices.length);
+            for (Device d : devices) {
+                deviceList.put(((JoclDevice) d).id);
+            }
+            deviceCount = devices.length;
+            deviceList.rewind();
+        }
+        int ret = cl.clBuildProgram(program, deviceCount, deviceList, args, null);
+        if (ret != CL.CL_SUCCESS) {
+            String log = Log();
+            LOG.log(Level.WARNING, "Unable to compile program:\n{0}", log);
+            if (ret == CL.CL_BUILD_PROGRAM_FAILURE) {
+                throw new KernelCompilationException("Failed to build program", ret, log);
+            } else {
+                Utils.checkError(ret, "clBuildProgram");
+            }
+        } else {
+            LOG.log(Level.INFO, "Program compiled:\n{0}", Log());
+        }
+    }
+    
+    private String Log(long device) {
+        Utils.pointers[0].rewind();
+        int ret = cl.clGetProgramBuildInfo(program, device, CL.CL_PROGRAM_BUILD_LOG, 0, null, Utils.pointers[0]);
+        Utils.checkError(ret, "clGetProgramBuildInfo");
+        int count = (int) Utils.pointers[0].get(0);
+        final ByteBuffer buffer = newDirectByteBuffer(count);
+        ret = cl.clGetProgramBuildInfo(program, device, CL.CL_PROGRAM_BUILD_LOG, buffer.capacity(), buffer, null);
+        Utils.checkError(ret, "clGetProgramBuildInfo");
+        return CLUtil.clString2JavaString(buffer, count);
+    }
+    
+    private String Log() {
+        StringBuilder str = new StringBuilder();
+        for (JoclDevice device : context.getDevices()) {
+            long d = device.id;
+            str.append(device.getName()).append(":\n");
+            str.append(Log(d));
+            str.append('\n');
+        }
+        return str.toString();
+    }
+
+    @Override
+    public Kernel createKernel(String name) {
+        Utils.errorBuffer.rewind();
+        long kernel = cl.clCreateKernel(program, name, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateKernel");
+        return new JoclKernel(kernel);
+    }
+
+    @Override
+    public Kernel[] createAllKernels() {
+        Utils.tempBuffers[0].b16i.rewind();
+        int ret = cl.clCreateKernelsInProgram(program, 0, null, Utils.tempBuffers[0].b16i);
+        Utils.checkError(ret, "clCreateKernelsInProgram");
+        int count = Utils.tempBuffers[0].b16i.get(0);
+        PointerBuffer buf = PointerBuffer.allocateDirect(count);
+        ret = cl.clCreateKernelsInProgram(program, count, buf, null);
+        Utils.checkError(ret, "clCreateKernelsInProgram");
+        Kernel[] kx = new Kernel[count];
+        for (int i=0; i<count; ++i) {
+            kx[i] = new JoclKernel(buf.get());
+        }
+        return kx;
+    }
+
+    @Override
+    public ByteBuffer getBinary(Device d) {
+        
+        JoclDevice device = (JoclDevice) d;
+        
+        Utils.tempBuffers[0].b16i.rewind();
+        int ret = cl.clGetProgramInfo(program, CL.CL_PROGRAM_NUM_DEVICES, 4, Utils.tempBuffers[0].b16i, null);
+        Utils.checkError(ret, "clGetProgramInfo: CL_PROGRAM_NUM_DEVICES");
+        int numDevices = Utils.tempBuffers[0].b16i.get(0);
+        
+        PointerBuffer devices = PointerBuffer.allocateDirect(numDevices);
+        ret = cl.clGetProgramInfo(program, CL.CL_PROGRAM_DEVICES, numDevices * PointerBuffer.ELEMENT_SIZE, devices.getBuffer(), null);
+        Utils.checkError(ret, "clGetProgramInfo: CL_PROGRAM_DEVICES");
+        int index = -1;
+        for (int i=0; i<numDevices; ++i) {
+            if (devices.get(i) == device.id) {
+                index = i;
+            }
+        }
+        if (index == -1) {
+             throw new com.jme3.opencl.OpenCLException("Program was not built against the specified device "+device);
+        }
+        
+        final PointerBuffer sizes = PointerBuffer.allocateDirect(numDevices);
+        ret = cl.clGetProgramInfo(program, CL.CL_PROGRAM_BINARY_SIZES, numDevices * PointerBuffer.ELEMENT_SIZE, sizes.getBuffer(), null);
+        Utils.checkError(ret, "clGetProgramInfo: CL_PROGRAM_BINARY_SIZES");
+
+        final ByteBuffer binaries = Buffers.newDirectByteBuffer((int) sizes.get(index));
+        final PointerBuffer addresses = PointerBuffer.allocateDirect(numDevices);
+        for (int i=0; i<numDevices; ++i) {
+            if (index == i) {
+                addresses.referenceBuffer(binaries);
+            } else {
+                addresses.put(0);
+            }
+        }
+        addresses.rewind();
+
+        ret = cl.clGetProgramInfo(program, CL.CL_PROGRAM_BINARIES, numDevices * PointerBuffer.ELEMENT_SIZE, addresses.getBuffer(), null);
+        Utils.checkError(ret, "clGetProgramInfo: CL_PROGRAM_BINARIES");
+
+        return binaries;
+    }
+
+    private static class ReleaserImpl implements ObjectReleaser {
+        private long program;
+        private ReleaserImpl(long program) {
+            this.program = program;
+        }
+        @Override
+        public void release() {
+            if (program != 0) {
+                int ret = CLPlatform.getLowLevelCLInterface().clReleaseProgram(program);
+                program = 0;
+                Utils.reportError(ret, "clReleaseProgram");
+            }
+        }
+    }
+}

+ 162 - 0
jme3-jogl/src/main/java/com/jme3/opencl/jocl/Utils.java

@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.jocl;
+
+import com.jme3.opencl.MappingAccess;
+import com.jme3.opencl.MemoryAccess;
+import com.jme3.opencl.OpenCLException;
+import com.jme3.util.BufferUtils;
+import com.jogamp.common.nio.PointerBuffer;
+import com.jogamp.opencl.CLEventList;
+import com.jogamp.opencl.CLException;
+import com.jogamp.opencl.CLMemory;
+import com.jogamp.opencl.CLVersion;
+import com.jogamp.opencl.llb.CL;
+import java.lang.reflect.Field;
+import java.nio.*;
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author shaman
+ */
+public class Utils {
+    private static final Logger LOG = Logger.getLogger(Utils.class.getName());
+    private Utils() {}
+    
+    public static int getMajorVersion(String version, String prefix) {
+        String s = version.substring(prefix.length());
+        return Integer.parseInt(s);
+    }
+    
+    public static int getMinorVersion(String version, String prefix) {
+        String s = version.substring(prefix.length());
+        int major = Integer.parseInt(s);
+        s = s.substring((int) (Math.log10(major) + 2));
+        return Integer.parseInt(s);
+    }
+    
+    public static final class TempBuffer {
+        public final ByteBuffer b16;
+        public final ShortBuffer b16s;
+        public final IntBuffer b16i;
+        public final LongBuffer b16l;
+        public final FloatBuffer b16f;
+        public final DoubleBuffer b16d;
+        public TempBuffer() {
+            b16 = BufferUtils.createByteBuffer(16);
+            b16s = b16.asShortBuffer();
+            b16i = b16.asIntBuffer();
+            b16l = b16.asLongBuffer();
+            b16f = b16.asFloatBuffer();
+            b16d = b16.asDoubleBuffer();
+        }
+    }
+    public static final ByteBuffer b80; //needed for ImageDescriptor
+    public static final LongBuffer b80l;
+    public static final FloatBuffer b80f;
+    public static final TempBuffer[] tempBuffers = new TempBuffer[8];
+    public static final PointerBuffer[] pointers = new PointerBuffer[8];
+    static {
+        for (int i=0; i<8; ++i) {
+            tempBuffers[i] = new TempBuffer();
+            pointers[i] = PointerBuffer.allocateDirect(4);
+        }
+        errorBuffer = BufferUtils.createIntBuffer(1);
+        b80 = BufferUtils.createByteBuffer(80);
+        b80l = b80.asLongBuffer();
+        b80f = b80.asFloatBuffer();
+    }
+    
+    public static IntBuffer errorBuffer;
+    public static void checkError(IntBuffer errorBuffer, String callName) {
+        checkError(errorBuffer.get(0), callName);
+    }
+    public static void checkError(int error, String callName) {
+        if (error != CL.CL_SUCCESS) {
+            String errname = getErrorName(error);
+            if (errname == null) {
+                errname = "UNKNOWN";
+            }
+            throw new OpenCLException("OpenCL error in " + callName + ": " + errname + " (0x" + Integer.toHexString(error) + ")", error);
+        }
+    }
+    
+    public static void reportError(int error, String callName) {
+        if (error != CL.CL_SUCCESS) {
+            String errname = getErrorName(error);
+            if (errname == null) {
+                errname = "UNKNOWN";
+            }
+            LOG.log(Level.WARNING, "OpenCL error in {0}: {1} (0x{2})", new Object[]{callName, errname, Integer.toHexString(error)});
+        }
+    }
+    
+    public static String getErrorName(int code) {
+        return CLException.resolveErrorCode(code);
+    }
+    
+     public static long getMemoryAccessFlags(MemoryAccess ma) {
+        switch (ma) {
+            case READ_ONLY: return CL.CL_MEM_READ_ONLY;
+            case WRITE_ONLY: return CL.CL_MEM_WRITE_ONLY;
+            case READ_WRITE: return CL.CL_MEM_READ_WRITE;
+            default: throw new IllegalArgumentException("Unknown memory access: "+ma);
+        }
+    }
+    public static MemoryAccess getMemoryAccessFromFlag(long flag) {
+        if ((flag & CL.CL_MEM_READ_WRITE) > 0) {
+            return MemoryAccess.READ_WRITE;
+        }
+        if ((flag & CL.CL_MEM_READ_ONLY) > 0) {
+            return MemoryAccess.READ_ONLY;
+        }
+        if ((flag & CL.CL_MEM_WRITE_ONLY) > 0) {
+            return MemoryAccess.WRITE_ONLY;
+        }
+        throw new OpenCLException("Unknown memory access flag: "+flag);
+    }
+    
+    public static long getMappingAccessFlags(MappingAccess ma) {
+        switch (ma) {
+            case MAP_READ_ONLY: return CL.CL_MAP_READ;
+            case MAP_READ_WRITE: return CL.CL_MAP_READ | CL.CL_MAP_WRITE;
+            case MAP_WRITE_ONLY: return CL.CL_MAP_WRITE;
+            case MAP_WRITE_INVALIDATE: return CL.CL_MAP_WRITE; //MAP_WRITE_INVALIDATE_REGION not supported
+            default: throw new IllegalArgumentException("Unknown mapping access: "+ma);
+        }
+    }
+
+}

+ 115 - 0
jme3-jogl/src/main/java/com/jme3/system/jogl/JoglContext.java

@@ -35,6 +35,12 @@ package com.jme3.system.jogl;
 import com.jme3.input.JoyInput;
 import com.jme3.input.KeyInput;
 import com.jme3.input.MouseInput;
+import com.jme3.opencl.Context;
+import com.jme3.opencl.DefaultPlatformChooser;
+import com.jme3.opencl.Device;
+import com.jme3.opencl.PlatformChooser;
+import com.jme3.opencl.jocl.JoclDevice;
+import com.jme3.opencl.jocl.JoclPlatform;
 import com.jme3.renderer.Renderer;
 import com.jme3.renderer.RendererException;
 import com.jme3.renderer.jogl.JoglGL;
@@ -55,6 +61,10 @@ import com.jme3.system.JmeContext;
 import com.jme3.system.NanoTimer;
 import com.jme3.system.SystemListener;
 import com.jme3.system.Timer;
+import com.jogamp.opencl.CLDevice;
+import com.jogamp.opencl.CLPlatform;
+import com.jogamp.opencl.gl.CLGLContext;
+import com.jogamp.opencl.llb.CL;
 
 import java.nio.IntBuffer;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -64,6 +74,8 @@ import java.util.logging.Logger;
 import com.jogamp.opengl.GL;
 import com.jogamp.opengl.GL2GL3;
 import com.jogamp.opengl.GLContext;
+import java.util.ArrayList;
+import java.util.List;
 
 public abstract class JoglContext implements JmeContext {
 
@@ -84,6 +96,8 @@ public abstract class JoglContext implements JmeContext {
     protected MouseInput mouseInput;
     protected JoyInput joyInput;
 
+    protected com.jme3.opencl.Context clContext;
+    
     @Override
 	public void setSystemListener(SystemListener listener){
         this.listener = listener;
@@ -209,6 +223,101 @@ public abstract class JoglContext implements JmeContext {
         if (joyInput != null) {
             joyInput.initialize();
         }
+        
+        if (settings.isOpenCLSupport()) {
+            initOpenCL();
+        }
+    }
+    
+    @SuppressWarnings("unchecked")
+    protected void initOpenCL() {
+        logger.info("Initialize OpenCL with JOGL");
+        
+        //load platforms and devices
+        StringBuilder platformInfos = new StringBuilder();
+        ArrayList<JoclPlatform> platforms = new ArrayList<JoclPlatform>();
+        for (CLPlatform p : CLPlatform.listCLPlatforms()) {
+            platforms.add(new JoclPlatform(p));
+        }
+        platformInfos.append("Available OpenCL platforms:");
+        for (int i=0; i<platforms.size(); ++i) {
+            JoclPlatform platform = platforms.get(i);
+            platformInfos.append("\n * Platform ").append(i+1);
+            platformInfos.append("\n *   Name: ").append(platform.getName());
+            platformInfos.append("\n *   Vendor: ").append(platform.getVendor());
+            platformInfos.append("\n *   Version: ").append(platform.getVersion());
+            platformInfos.append("\n *   Profile: ").append(platform.getProfile());
+            platformInfos.append("\n *   Supports interop: ").append(platform.hasOpenGLInterop());
+            List<JoclDevice> devices = platform.getDevices();
+            platformInfos.append("\n *   Available devices:");
+            for (int j=0; j<devices.size(); ++j) {
+                JoclDevice device = devices.get(j);
+                platformInfos.append("\n *    * Device ").append(j+1);
+                platformInfos.append("\n *    *   Name: ").append(device.getName());
+                platformInfos.append("\n *    *   Vendor: ").append(device.getVendor());
+                platformInfos.append("\n *    *   Version: ").append(device.getVersion());
+                platformInfos.append("\n *    *   Profile: ").append(device.getProfile());
+                platformInfos.append("\n *    *   Compiler version: ").append(device.getCompilerVersion());
+                platformInfos.append("\n *    *   Device type: ").append(device.getDeviceType());
+                platformInfos.append("\n *    *   Compute units: ").append(device.getComputeUnits());
+                platformInfos.append("\n *    *   Work group size: ").append(device.getMaxiumWorkItemsPerGroup());
+                platformInfos.append("\n *    *   Global memory: ").append(device.getGlobalMemorySize()).append("B");
+                platformInfos.append("\n *    *   Local memory: ").append(device.getLocalMemorySize()).append("B");
+                platformInfos.append("\n *    *   Constant memory: ").append(device.getMaximumConstantBufferSize()).append("B");
+                platformInfos.append("\n *    *   Supports double: ").append(device.hasDouble());
+                platformInfos.append("\n *    *   Supports half floats: ").append(device.hasHalfFloat());
+                platformInfos.append("\n *    *   Supports writable 3d images: ").append(device.hasWritableImage3D());
+                platformInfos.append("\n *    *   Supports interop: ").append(device.hasOpenGLInterop());
+            }
+        }
+        logger.info(platformInfos.toString());
+        
+        //choose devices
+        PlatformChooser chooser = null;
+        if (settings.getOpenCLPlatformChooser() != null) {
+            try {
+                chooser = (PlatformChooser) Class.forName(settings.getOpenCLPlatformChooser()).newInstance();
+            } catch (Exception ex) {
+                logger.log(Level.WARNING, "unable to instantiate custom PlatformChooser", ex);
+            }
+        }
+        if (chooser == null) {
+            chooser = new DefaultPlatformChooser();
+        }
+        List<? extends Device> choosenDevices = chooser.chooseDevices(platforms);
+        List<CLDevice> devices = new ArrayList<>(choosenDevices.size());
+        JoclPlatform platform = null;
+        for (Device d : choosenDevices) {
+            if (!(d instanceof JoclDevice)) {
+                logger.log(Level.SEVERE, "attempt to return a custom Device implementation from PlatformChooser: {0}", d);
+                return;
+            }
+            JoclDevice ld = (JoclDevice) d;
+            if (platform == null) {
+                platform = ld.getPlatform();
+            } else if (platform != ld.getPlatform()) {
+                logger.severe("attempt to use devices from different platforms");
+                return;
+            }
+            devices.add(ld.getDevice());
+        }
+        if (devices.isEmpty()) {
+            logger.warning("no devices specified, no OpenCL context created");
+            return;
+        }
+        logger.log(Level.INFO, "chosen platform: {0}", platform.getName());
+        logger.log(Level.INFO, "chosen devices: {0}", choosenDevices);
+        
+        //create context
+        try {
+            CLGLContext c = CLGLContext.create(GLContext.getCurrent(), devices.toArray(new CLDevice[devices.size()]));
+            clContext = new com.jme3.opencl.jocl.JoclContext(c, (List<JoclDevice>) choosenDevices);
+        } catch (Exception ex) {
+            logger.log(Level.SEVERE, "Unable to create OpenCL context", ex);
+            return;
+        }
+        
+        logger.info("OpenCL context created");
     }
 
     public void internalCreate() {
@@ -266,4 +375,10 @@ public abstract class JoglContext implements JmeContext {
         }
         return samples;
     }
+
+    @Override
+    public Context getOpenCLContext() {
+        return clContext;
+    }
+    
 }

+ 236 - 0
jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglBuffer.java

@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.lwjgl;
+
+import com.jme3.opencl.*;
+import java.nio.ByteBuffer;
+import org.lwjgl.opencl.*;
+
+/**
+ *
+ * @author shaman
+ */
+public class LwjglBuffer extends Buffer {
+
+    private final CLMem buffer;
+
+    public LwjglBuffer(CLMem buffer) {
+        super(new ReleaserImpl(buffer));
+        this.buffer = buffer;
+    }
+    public CLMem getBuffer() {
+        return buffer;
+    }
+    
+    @Override
+    public long getSize() {
+        return buffer.getInfoSize(CL10.CL_MEM_SIZE);
+    }
+
+    @Override
+    public MemoryAccess getMemoryAccessFlags() {
+        return Utils.getMemoryAccessFromFlag(buffer.getInfoLong(CL10.CL_MEM_FLAGS));
+    }
+
+    @Override
+    public void read(CommandQueue queue, ByteBuffer dest, long size, long offset) {
+        //Note: LWJGL does not support the size parameter, I have to set the buffer limit
+        dest.limit((int) (dest.position() + size));
+        int ret = CL10.clEnqueueReadBuffer(((LwjglCommandQueue)queue).getQueue(), 
+                buffer, CL10.CL_TRUE, offset, dest, null, null);
+        Utils.checkError(ret, "clEnqueueReadBuffer");
+    }
+
+    @Override
+    public Event readAsync(CommandQueue queue, ByteBuffer dest, long size, long offset) {
+        //Note: LWJGL does not support the size parameter, I have to set the buffer limit
+        dest.limit((int) (dest.position() + size));
+        Utils.pointerBuffers[0].rewind();
+        CLCommandQueue q = ((LwjglCommandQueue)queue).getQueue();
+        int ret = CL10.clEnqueueReadBuffer(q, buffer, CL10.CL_FALSE, offset, dest, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueReadBuffer");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(q.getCLEvent(event));
+    }
+
+    @Override
+    public void write(CommandQueue queue, ByteBuffer src, long size, long offset) {
+        //Note: LWJGL does not support the size parameter, I have to set the buffer limit
+        src.limit((int) (src.position() + size));
+        CLCommandQueue q = ((LwjglCommandQueue)queue).getQueue();
+        int ret = CL10.clEnqueueWriteBuffer(q, buffer, CL10.CL_TRUE, offset, src, null, null);
+        Utils.checkError(ret, "clEnqueueWriteBuffer");
+    }
+
+    @Override
+    public Event writeAsync(CommandQueue queue, ByteBuffer src, long size, long offset) {
+        //Note: LWJGL does not support the size parameter, I have to set the buffer limit
+        src.limit((int) (src.position() + size));
+        Utils.pointerBuffers[0].rewind();
+        CLCommandQueue q = ((LwjglCommandQueue)queue).getQueue();
+        int ret = CL10.clEnqueueWriteBuffer(q, buffer, CL10.CL_FALSE, offset, src, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueWriteBuffer");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(q.getCLEvent(event));
+    }
+
+    @Override
+    public void copyTo(CommandQueue queue, Buffer dest, long size, long srcOffset, long destOffset) {
+        CLCommandQueue q = ((LwjglCommandQueue)queue).getQueue();
+        Utils.pointerBuffers[0].rewind();
+        int ret = CL10.clEnqueueCopyBuffer(q, buffer, ((LwjglBuffer) dest).buffer, srcOffset, destOffset, size, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueCopyBuffer");
+        long event = Utils.pointerBuffers[0].get(0);
+        ret = CL10.clWaitForEvents(q.getCLEvent(event));
+        Utils.checkError(ret, "clWaitForEvents");
+    }
+
+    @Override
+    public Event copyToAsync(CommandQueue queue, Buffer dest, long size, long srcOffset, long destOffset) {
+        CLCommandQueue q = ((LwjglCommandQueue)queue).getQueue();
+        Utils.pointerBuffers[0].rewind();
+        int ret = CL10.clEnqueueCopyBuffer(q, buffer, ((LwjglBuffer) dest).buffer, srcOffset, destOffset, size, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueCopyBuffer");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(q.getCLEvent(event));
+    }
+
+    @Override
+    public ByteBuffer map(CommandQueue queue, long size, long offset, MappingAccess access) {
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        long flags = Utils.getMappingAccessFlags(access);
+        Utils.errorBuffer.rewind();
+        ByteBuffer b = CL10.clEnqueueMapBuffer(q, buffer, CL10.CL_TRUE, flags, offset, size, null, null, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clEnqueueMapBuffer");
+        return b;
+    }
+
+    @Override
+    public void unmap(CommandQueue queue, ByteBuffer ptr) {
+        ptr.position(0);
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        Utils.pointerBuffers[0].rewind();
+        int ret = CL10.clEnqueueUnmapMemObject(q, buffer, ptr, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueUnmapMemObject");
+        long event = Utils.pointerBuffers[0].get(0);
+        ret = CL10.clWaitForEvents(q.getCLEvent(event));
+        Utils.checkError(ret, "clWaitForEvents");
+    }
+
+    @Override
+    public com.jme3.opencl.Buffer.AsyncMapping mapAsync(CommandQueue queue, long size, long offset, MappingAccess access) {
+        Utils.pointerBuffers[0].rewind();
+        Utils.errorBuffer.rewind();
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        long flags = Utils.getMappingAccessFlags(access);
+        ByteBuffer buf = CL10.clEnqueueMapBuffer(q, buffer, CL10.CL_FALSE, flags, offset, size, null, Utils.pointerBuffers[0], Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clEnqueueMapBuffer");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new com.jme3.opencl.Buffer.AsyncMapping(new LwjglEvent(q.getCLEvent(event)), buf);
+    }
+
+    @Override
+    public Event fillAsync(CommandQueue queue, ByteBuffer pattern, long size, long offset) {
+        Utils.pointerBuffers[0].rewind();
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL12.clEnqueueFillBuffer(q, buffer, pattern, offset, size, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueFillBuffer");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(q.getCLEvent(event));
+    }
+
+    @Override
+    public Event copyToImageAsync(CommandQueue queue, Image dest, long srcOffset, long[] destOrigin, long[] destRegion) {
+        if (destOrigin.length!=3 || destRegion.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointerBuffers[0].rewind();
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[2].rewind();
+        Utils.pointerBuffers[1].put(destOrigin).position(0);
+        Utils.pointerBuffers[2].put(destRegion).position(0);
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10.clEnqueueCopyBufferToImage(q, buffer, ((LwjglImage) dest).getImage(), 
+                srcOffset, Utils.pointerBuffers[1], Utils.pointerBuffers[2], null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueCopyBufferToImage");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(q.getCLEvent(event));
+    }
+
+    @Override
+    public Event acquireBufferForSharingAsync(CommandQueue queue) {
+        Utils.pointerBuffers[0].rewind();
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10GL.clEnqueueAcquireGLObjects(q, buffer, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueAcquireGLObjects");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(q.getCLEvent(event));
+    }
+    @Override
+    public void acquireBufferForSharingNoEvent(CommandQueue queue) {
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10GL.clEnqueueAcquireGLObjects(q, buffer, null, null);
+        Utils.checkError(ret, "clEnqueueAcquireGLObjects");
+    }
+
+    @Override
+    public Event releaseBufferForSharingAsync(CommandQueue queue) {
+        Utils.pointerBuffers[0].rewind();
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10GL.clEnqueueReleaseGLObjects(q, buffer, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueReleaseGLObjects");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(q.getCLEvent(event));
+    }
+    @Override
+    public void releaseBufferForSharingNoEvent(CommandQueue queue) {
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10GL.clEnqueueReleaseGLObjects(q, buffer, null, null);
+        Utils.checkError(ret, "clEnqueueReleaseGLObjects");
+    }
+
+    private static class ReleaserImpl implements ObjectReleaser {
+        private CLMem mem;
+        private ReleaserImpl(CLMem mem) {
+            this.mem = mem;
+        }
+        @Override
+        public void release() {
+            if (mem != null) {
+                int ret = CL10.clReleaseMemObject(mem);
+                mem = null;
+                Utils.reportError(ret, "clReleaseMemObject");
+            }
+        }
+        
+    }
+}

+ 82 - 0
jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglCommandQueue.java

@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.lwjgl;
+
+import com.jme3.opencl.CommandQueue;
+import com.jme3.opencl.OpenCLObjectManager;
+import org.lwjgl.opencl.CL10;
+import org.lwjgl.opencl.CLCommandQueue;
+
+/**
+ *
+ * @author shaman
+ */
+public class LwjglCommandQueue extends CommandQueue {
+
+    private final CLCommandQueue queue;
+
+    public LwjglCommandQueue(CLCommandQueue queue) {
+        super(new ReleaserImpl(queue));
+        this.queue = queue;
+    }
+    
+    public CLCommandQueue getQueue() {
+        return queue;
+    }
+    
+    @Override
+    public void flush() {
+        int ret = CL10.clFlush(queue);
+        Utils.checkError(ret, "clFlush");
+    }
+
+    @Override
+    public void finish() {
+        int ret = CL10.clFinish(queue);
+        Utils.checkError(ret, "clFinish");
+    }
+    
+    private static class ReleaserImpl implements ObjectReleaser {
+        private CLCommandQueue queue;
+        private ReleaserImpl(CLCommandQueue queue) {
+            this.queue = queue;
+        }
+        @Override
+        public void release() {
+            if (queue != null) {
+                int ret = CL10.clReleaseCommandQueue(queue);
+                queue = null;
+                Utils.reportError(ret, "clReleaseCommandQueue");
+            }
+        }
+    }
+}

+ 240 - 0
jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglContext.java

@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.lwjgl;
+
+import com.jme3.opencl.*;
+import com.jme3.opencl.Context;
+import com.jme3.opencl.Image.ImageDescriptor;
+import com.jme3.opencl.Image.ImageFormat;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Texture;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.lwjgl.BufferUtils;
+import org.lwjgl.opencl.*;
+import org.lwjgl.opengl.*;
+
+/**
+ *
+ * @author shaman
+ */
+public class LwjglContext extends Context {
+    private static final Logger LOG = Logger.getLogger(LwjglContext.class.getName());
+    private final CLContext context;
+    private final List<LwjglDevice> devices;
+
+    public LwjglContext(CLContext context, List<LwjglDevice> devices) {
+        super(new ReleaserImpl(context, devices));
+        this.context = context;
+        this.devices = devices;
+    }
+
+    public CLContext getContext() {
+        return context;
+    }
+
+    @Override
+    public List<LwjglDevice> getDevices() {
+        return devices;
+    }
+
+    @Override
+    @SuppressWarnings("element-type-mismatch")
+    public CommandQueue createQueue(Device device) {
+        assert (devices.contains(device)); //this also ensures that device is a LwjglDevice
+        CLDevice d = ((LwjglDevice) device).getDevice();
+        long properties = 0;
+        CLCommandQueue q = CL10.clCreateCommandQueue(context, d, properties, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateCommandQueue");
+        return new LwjglCommandQueue(q);
+    }
+    
+    @Override
+    public Buffer createBuffer(long size, MemoryAccess access) {
+        long flags = Utils.getMemoryAccessFlags(access);
+        CLMem mem = CL10.clCreateBuffer(context, flags, size, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateBuffer");
+        return new LwjglBuffer(mem);
+    }
+
+    @Override
+    public Buffer createBufferFromHost(ByteBuffer data, MemoryAccess access) {
+        long flags = Utils.getMemoryAccessFlags(access);
+        flags |= CL10.CL_MEM_USE_HOST_PTR;
+        CLMem mem = CL10.clCreateBuffer(context, flags, data, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateBuffer");
+        return new LwjglBuffer(mem);
+    }
+
+    @Override
+    public Image createImage(MemoryAccess access, ImageFormat format, ImageDescriptor descr) {
+        long memFlags = Utils.getMemoryAccessFlags(access);
+        Utils.errorBuffer.rewind();
+        //fill image format
+        Utils.tempBuffers[0].b16i.rewind();
+        Utils.tempBuffers[0].b16i.put(LwjglImage.decodeImageChannelOrder(format.channelOrder))
+                .put(LwjglImage.decodeImageChannelType(format.channelType));
+        Utils.tempBuffers[0].b16.rewind();
+        //fill image desc
+        Utils.b80l.rewind();
+        Utils.b80l.put(LwjglImage.decodeImageType(descr.type))
+                .put(descr.width).put(descr.height).put(descr.depth)
+                .put(descr.arraySize).put(descr.rowPitch).put(descr.slicePitch)
+                .put(0).put(0).put(0);
+        Utils.b80.rewind();
+        //create image
+        CLMem mem = CL12.clCreateImage(context, memFlags, Utils.tempBuffers[0].b16, Utils.b80, descr.hostPtr, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateImage");
+        return new LwjglImage(mem);
+    }
+
+    @Override
+    public ImageFormat[] querySupportedFormats(MemoryAccess access, Image.ImageType type) {
+        long memFlags = Utils.getMemoryAccessFlags(access);
+        int typeFlag = LwjglImage.decodeImageType(type);
+        Utils.tempBuffers[0].b16i.rewind();
+        //query count
+        int ret = CL10.clGetSupportedImageFormats(context, memFlags, typeFlag, null, Utils.tempBuffers[0].b16i);
+        Utils.checkError(ret, "clGetSupportedImageFormats");
+        int count = Utils.tempBuffers[0].b16i.get(0);
+        if (count == 0) {
+            return new ImageFormat[0];
+        }
+        //get formats
+        ByteBuffer formatsB = BufferUtils.createByteBuffer(count * 8);
+        ret = CL10.clGetSupportedImageFormats(context, memFlags, typeFlag, formatsB, null);
+        Utils.checkError(ret, "clGetSupportedImageFormats");
+        //convert formats
+        ImageFormat[] formats = new ImageFormat[count];
+        IntBuffer formatsBi = formatsB.asIntBuffer();
+        formatsBi.rewind();
+        for (int i=0; i<count; ++i) {
+            Image.ImageChannelOrder channelOrder = LwjglImage.encodeImageChannelOrder(formatsBi.get());
+            Image.ImageChannelType channelType = LwjglImage.encodeImageChannelType(formatsBi.get());
+            formats[i] = new ImageFormat(channelOrder, channelType);
+        }
+        return formats;
+    }
+
+    @Override
+    public Buffer bindVertexBuffer(VertexBuffer vb, MemoryAccess access) {
+        int id = vb.getId();
+        if (id == -1) {
+            throw new IllegalArgumentException("vertex buffer was not yet uploaded to the GPU or is CPU only");
+        }
+        long flags = Utils.getMemoryAccessFlags(access);
+        Utils.errorBuffer.rewind();
+        CLMem mem = CL10GL.clCreateFromGLBuffer(context, flags, id, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateFromGLBuffer");
+        return new LwjglBuffer(mem);
+    }
+
+    @Override
+    public Image bindImage(com.jme3.texture.Image image, Texture.Type textureType, int miplevel, MemoryAccess access) {
+        int imageID = image.getId();
+        if (imageID == -1) {
+            throw new IllegalArgumentException("image was not yet uploaded to the GPU");
+        }
+        long memFlags = Utils.getMemoryAccessFlags(access);
+        int textureTarget = convertTextureType(textureType);
+        Utils.errorBuffer.rewind();
+        CLMem mem = CL12GL.clCreateFromGLTexture(context, memFlags, textureTarget, miplevel, imageID, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateFromGLTexture");
+        return new LwjglImage(mem);
+    }
+
+    @Override
+    protected Image bindPureRenderBuffer(FrameBuffer.RenderBuffer buffer, MemoryAccess access) {
+        int renderbuffer = buffer.getId();
+        if (renderbuffer == -1) {
+            throw new IllegalArgumentException("renderbuffer was not yet uploaded to the GPU");
+        }
+        long memFlags = Utils.getMemoryAccessFlags(access);
+        Utils.errorBuffer.rewind();
+        CLMem mem = CL10GL.clCreateFromGLRenderbuffer(context, memFlags, renderbuffer, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateFromGLRenderbuffer");
+        return new LwjglImage(mem);
+    }
+    
+    private int convertTextureType(Texture.Type textureType) {
+        switch (textureType) {
+            case TwoDimensional: return GL11.GL_TEXTURE_2D;
+            case TwoDimensionalArray: return GL30.GL_TEXTURE_2D_ARRAY;
+            case ThreeDimensional: return GL12.GL_TEXTURE_3D;
+            case CubeMap: return GL13.GL_TEXTURE_CUBE_MAP;
+            default: throw new IllegalArgumentException("unknown texture type "+textureType);
+        }
+    }
+
+    @Override
+    public Program createProgramFromSourceCode(String sourceCode) {
+        LOG.log(Level.FINE, "Create program from source:\n{0}", sourceCode);
+        Utils.errorBuffer.rewind();
+        CLProgram p = CL10.clCreateProgramWithSource(context, sourceCode, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateProgramWithSource");
+        return new LwjglProgram(p, this);
+    }
+
+    @Override
+    public Program createProgramFromBinary(ByteBuffer binaries, Device device) {
+        Utils.errorBuffer.rewind();
+        Utils.tempBuffers[0].b16i.rewind();
+        CLProgram p = CL10.clCreateProgramWithBinary(context, ((LwjglDevice) device).device, 
+                binaries, Utils.tempBuffers[0].b16i, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateProgramWithBinary");
+        Utils.checkError(Utils.tempBuffers[0].b16i, "clCreateProgramWithBinary");
+        return new LwjglProgram(p, this);
+    }
+
+    private static class ReleaserImpl implements ObjectReleaser {
+        private CLContext context;
+        private final List<LwjglDevice> devices;
+        private ReleaserImpl(CLContext mem, List<LwjglDevice> devices) {
+            this.context = mem;
+            this.devices = devices;
+        }
+        @Override
+        public void release() {
+            if (context != null) {
+                int ret = CL10.clReleaseContext(context);
+                context = null;
+                devices.clear();
+                Utils.reportError(ret, "clReleaseMemObject");
+            }
+        }
+        
+    }
+}

+ 293 - 0
jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglDevice.java

@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.lwjgl;
+
+import com.jme3.opencl.Device;
+import com.jme3.opencl.Platform;
+import java.util.Arrays;
+import java.util.Collection;
+import org.lwjgl.opencl.CL10;
+import org.lwjgl.opencl.CL11;
+import org.lwjgl.opencl.CLDevice;
+
+/**
+ *
+ * @author shaman
+ */
+public final class LwjglDevice implements Device {
+
+    final CLDevice device;
+    final LwjglPlatform platform;
+
+    public LwjglDevice(CLDevice device, LwjglPlatform platform) {
+        this.device = device;
+        this.platform = platform;
+    }
+    
+    public CLDevice getDevice() {
+        return device;
+    }
+    
+    @Override
+    public LwjglPlatform getPlatform() {
+        return platform;
+    }
+
+    @Override
+    public DeviceType getDeviceType() {
+        int type = device.getInfoInt(CL10.CL_DEVICE_TYPE);
+        switch (type) {
+            case CL10.CL_DEVICE_TYPE_ACCELERATOR: return DeviceType.ACCELEARTOR;
+            case CL10.CL_DEVICE_TYPE_CPU: return DeviceType.CPU;
+            case CL10.CL_DEVICE_TYPE_GPU: return DeviceType.GPU;
+            default: return DeviceType.DEFAULT;
+        }
+    }
+
+    @Override
+    public int getVendorId() {
+        return device.getInfoInt(CL10.CL_DEVICE_VENDOR_ID);
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return device.getInfoBoolean(CL10.CL_DEVICE_AVAILABLE);
+    }
+
+    @Override
+    public boolean hasCompiler() {
+        return device.getInfoBoolean(CL10.CL_DEVICE_COMPILER_AVAILABLE);
+    }
+
+    @Override
+    public boolean hasDouble() {
+        return hasExtension("cl_khr_fp64");
+    }
+
+    @Override
+    public boolean hasHalfFloat() {
+        return hasExtension("cl_khr_fp16");
+    }
+
+    @Override
+    public boolean hasErrorCorrectingMemory() {
+        return device.getInfoBoolean(CL10.CL_DEVICE_ERROR_CORRECTION_SUPPORT);
+    }
+
+    @Override
+    public boolean hasUnifiedMemory() {
+        return device.getInfoBoolean(CL11.CL_DEVICE_HOST_UNIFIED_MEMORY);
+    }
+
+    @Override
+    public boolean hasImageSupport() {
+        return device.getInfoBoolean(CL10.CL_DEVICE_IMAGE_SUPPORT);
+    }
+    
+    @Override
+    public boolean hasWritableImage3D() {
+        return hasExtension("cl_khr_3d_image_writes");
+    }
+
+    @Override
+    public boolean hasOpenGLInterop() {
+        return hasExtension("cl_khr_gl_sharing");
+    }
+    
+    @Override
+    public boolean hasExtension(String extension) {
+        return getExtensions().contains(extension);
+    }
+
+    @Override
+    public Collection<? extends String> getExtensions() {
+        return Arrays.asList(device.getInfoString(CL10.CL_DEVICE_EXTENSIONS).split(" "));
+    }
+
+    @Override
+    public int getComputeUnits() {
+        return device.getInfoInt(CL10.CL_DEVICE_MAX_COMPUTE_UNITS);
+    }
+
+    @Override
+    public int getClockFrequency() {
+        return device.getInfoInt(CL10.CL_DEVICE_MAX_CLOCK_FREQUENCY);
+    }
+
+    @Override
+    public int getAddressBits() {
+        return device.getInfoInt(CL10.CL_DEVICE_ADDRESS_BITS);
+    }
+
+    @Override
+    public boolean isLittleEndian() {
+        return device.getInfoBoolean(CL10.CL_DEVICE_ENDIAN_LITTLE);
+    }
+
+    @Override
+    public long getMaximumWorkItemDimensions() {
+        return device.getInfoSize(CL10.CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS);
+    }
+
+    @Override
+    public long[] getMaximumWorkItemSizes() {
+        return device.getInfoSizeArray(CL10.CL_DEVICE_MAX_WORK_ITEM_SIZES);
+    }
+
+    @Override
+    public long getMaxiumWorkItemsPerGroup() {
+        return device.getInfoSize(CL10.CL_DEVICE_MAX_WORK_GROUP_SIZE);
+    }
+
+    @Override
+    public int getMaximumSamplers() {
+        return device.getInfoInt(CL10.CL_DEVICE_MAX_SAMPLERS);
+    }
+
+    @Override
+    public int getMaximumReadImages() {
+        return device.getInfoInt(CL10.CL_DEVICE_MAX_READ_IMAGE_ARGS);
+    }
+
+    @Override
+    public int getMaximumWriteImages() {
+        return device.getInfoInt(CL10.CL_DEVICE_MAX_WRITE_IMAGE_ARGS);
+    }
+
+    @Override
+    public long[] getMaximumImage2DSize() {
+        return new long[] {
+            device.getInfoSize(CL10.CL_DEVICE_IMAGE2D_MAX_WIDTH),
+            device.getInfoSize(CL10.CL_DEVICE_IMAGE2D_MAX_HEIGHT)
+        };
+    }
+
+    @Override
+    public long[] getMaximumImage3DSize() {
+        return new long[] {
+            device.getInfoSize(CL10.CL_DEVICE_IMAGE3D_MAX_WIDTH),
+            device.getInfoSize(CL10.CL_DEVICE_IMAGE3D_MAX_HEIGHT),
+            device.getInfoSize(CL10.CL_DEVICE_IMAGE3D_MAX_DEPTH)
+        };
+    }
+    
+    @Override
+    public long getMaximumAllocationSize() {
+        return device.getInfoLong(CL10.CL_DEVICE_MAX_MEM_ALLOC_SIZE);
+    }
+    
+    @Override
+    public long getGlobalMemorySize() {
+        return device.getInfoLong(CL10.CL_DEVICE_GLOBAL_MEM_SIZE);
+    }
+    
+    @Override
+    public long getLocalMemorySize() {
+        return device.getInfoLong(CL10.CL_DEVICE_LOCAL_MEM_SIZE);
+    }
+    
+    @Override
+    public long getMaximumConstantBufferSize() {
+        return device.getInfoLong(CL10.CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE);
+    }
+    
+    @Override
+    public int getMaximumConstantArguments() {
+        return device.getInfoInt(CL10.CL_DEVICE_MAX_CONSTANT_ARGS);
+    }
+
+    @Override
+    public String getProfile() {
+        return device.getInfoString(CL10.CL_DEVICE_PROFILE);
+    }
+
+    @Override
+    public String getVersion() {
+        return device.getInfoString(CL10.CL_DEVICE_VERSION);
+    }
+
+    @Override
+    public int getVersionMajor() {
+        return Utils.getMajorVersion(getVersion(), "OpenCL ");
+    }
+
+    @Override
+    public int getVersionMinor() {
+        return Utils.getMinorVersion(getVersion(), "OpenCL ");
+    }
+
+    @Override
+    public String getCompilerVersion() {
+        return device.getInfoString(CL11.CL_DEVICE_OPENCL_C_VERSION);
+    }
+
+    @Override
+    public int getCompilerVersionMajor() {
+        return Utils.getMajorVersion(getCompilerVersion(), "OpenCL C ");
+    }
+
+    @Override
+    public int getCompilerVersionMinor() {
+        return Utils.getMinorVersion(getCompilerVersion(), "OpenCL C ");
+    }
+
+    @Override
+    public String getDriverVersion() {
+        return device.getInfoString(CL10.CL_DRIVER_VERSION);
+    }
+
+    @Override
+    public int getDriverVersionMajor() {
+        return Utils.getMajorVersion(getDriverVersion(), "");
+    }
+
+    @Override
+    public int getDriverVersionMinor() {
+        return Utils.getMinorVersion(getDriverVersion(), "");
+    }
+
+    @Override
+    public String getName() {
+        return device.getInfoString(CL10.CL_DEVICE_NAME);
+    }
+
+    @Override
+    public String getVendor() {
+        return device.getInfoString(CL10.CL_DEVICE_VENDOR);
+    }
+
+    @Override
+    public String toString() {
+        return getName();
+    }
+    
+}

+ 100 - 0
jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglEvent.java

@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.lwjgl;
+
+import com.jme3.opencl.Event;
+import java.util.logging.Logger;
+import org.lwjgl.opencl.CL10;
+import org.lwjgl.opencl.CLEvent;
+
+/**
+ *
+ * @author shaman
+ */
+public class LwjglEvent extends Event {
+    private static final Logger LOG = Logger.getLogger(LwjglEvent.class.getName());
+    private CLEvent event;
+
+    public LwjglEvent(CLEvent event) {
+        super(new ReleaserImpl(event));
+        this.event = event;
+    }
+
+    public CLEvent getEvent() {
+        return event;
+    }
+
+    @Override
+    public void waitForFinished() {
+        if (event==null) {
+            return;
+        }
+        CL10.clWaitForEvents(event);
+        release(); //short cut to save resources
+    }
+
+    @Override
+    public boolean isCompleted() {
+        if (event==null) {
+            return true;
+        }
+        int status = event.getInfoInt(CL10.CL_EVENT_COMMAND_EXECUTION_STATUS);
+        if (status == CL10.CL_SUCCESS) {
+            release(); //short cut to save resources
+            return true;
+        } else if (status < 0) {
+            Utils.checkError(status, "EventStatus");
+            return false;
+        } else {
+            return false;
+        }
+    }
+
+    private static class ReleaserImpl implements ObjectReleaser {
+        private CLEvent event;
+
+        private ReleaserImpl(CLEvent event) {
+            this.event = event;
+        }
+        
+        @Override
+        public void release() {
+            if (event != null && event.isValid()) {
+                int ret = CL10.clReleaseEvent(event);
+                event = null;
+                Utils.reportError(ret, "clReleaseEvent");
+                LOG.finer("Event deleted");
+            }
+        }
+        
+    }
+}

+ 575 - 0
jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglImage.java

@@ -0,0 +1,575 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.lwjgl;
+
+import com.jme3.math.ColorRGBA;
+import com.jme3.opencl.*;
+import java.nio.ByteBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.lwjgl.opencl.*;
+import org.lwjgl.opencl.api.CLImageFormat;
+
+/**
+ *
+ * @author shaman
+ */
+public class LwjglImage extends Image {
+    private static final Logger LOG = Logger.getLogger(LwjglImage.class.getName());
+
+    private final CLMem image;
+
+    public LwjglImage(CLMem image) {
+        super(new ReleaserImpl(image));
+        this.image = image;
+    }
+
+    public CLMem getImage() {
+        return image;
+    }
+
+    public static int decodeImageChannelOrder(ImageChannelOrder order) {
+        switch (order) {
+            case A:
+                return CL10.CL_A;
+            case ARGB:
+                return CL10.CL_ARGB;
+            case BGRA:
+                return CL10.CL_BGRA;
+            case INTENSITY:
+                return CL10.CL_INTENSITY;
+            case LUMINANCE:
+                return CL10.CL_LUMINANCE;
+            case R:
+                return CL10.CL_R;
+            case RA:
+                return CL10.CL_RA;
+            case RG:
+                return CL10.CL_RG;
+            case RGB:
+                return CL10.CL_RGB;
+            case RGBA:
+                return CL10.CL_RGBA;
+            case RGBx:
+                return CL11.CL_RGBx;
+            case RGx:
+                return CL11.CL_RGx;
+            case Rx:
+                return CL11.CL_Rx;
+            default:
+                throw new IllegalArgumentException("unknown image channel order: " + order);
+        }
+    }
+
+    public static ImageChannelOrder encodeImageChannelOrder(int order) {
+        switch (order) {
+            case CL10.CL_A:
+                return ImageChannelOrder.A;
+            case CL10.CL_ARGB:
+                return ImageChannelOrder.ARGB;
+            case CL10.CL_BGRA:
+                return ImageChannelOrder.BGRA;
+            case CL10.CL_INTENSITY:
+                return ImageChannelOrder.INTENSITY;
+            case CL10.CL_LUMINANCE:
+                return ImageChannelOrder.LUMINANCE;
+            case CL10.CL_R:
+                return ImageChannelOrder.R;
+            case CL10.CL_RA:
+                return ImageChannelOrder.RA;
+            case CL10.CL_RG:
+                return ImageChannelOrder.RG;
+            case CL10.CL_RGB:
+                return ImageChannelOrder.RGB;
+            case CL10.CL_RGBA:
+                return ImageChannelOrder.RGBA;
+            case CL11.CL_RGBx:
+                return ImageChannelOrder.RGBx;
+            case CL11.CL_RGx:
+                return ImageChannelOrder.RGx;
+            case CL11.CL_Rx:
+                return ImageChannelOrder.Rx;
+            default:
+                //throw new com.jme3.opencl.OpenCLException("unknown image channel order id: " + order);
+                LOG.log(Level.WARNING, "Unknown image channel order id: {0}", order);
+                return null;
+        }
+    }
+
+    public static int decodeImageChannelType(ImageChannelType type) {
+        switch (type) {
+            case FLOAT:
+                return CL10.CL_FLOAT;
+            case HALF_FLOAT:
+                return CL10.CL_HALF_FLOAT;
+            case SIGNED_INT16:
+                return CL10.CL_SIGNED_INT16;
+            case SIGNED_INT32:
+                return CL10.CL_SIGNED_INT32;
+            case SIGNED_INT8:
+                return CL10.CL_SIGNED_INT8;
+            case SNORM_INT16:
+                return CL10.CL_SNORM_INT16;
+            case SNORM_INT8:
+                return CL10.CL_SNORM_INT8;
+            case UNORM_INT8:
+                return CL10.CL_UNORM_INT8;
+            case UNORM_INT_101010:
+                return CL10.CL_UNORM_INT_101010;
+            case UNORM_INT16:
+                return CL10.CL_UNORM_INT16;
+            case UNORM_SHORT_565:
+                return CL10.CL_UNORM_SHORT_565;
+            case UNORM_SHORT_555:
+                return CL10.CL_UNORM_SHORT_555;
+            case UNSIGNED_INT16:
+                return CL10.CL_UNSIGNED_INT16;
+            case UNSIGNED_INT32:
+                return CL10.CL_UNSIGNED_INT32;
+            case UNSIGNED_INT8:
+                return CL10.CL_UNSIGNED_INT8;
+            default:
+                throw new IllegalArgumentException("Unknown image channel type: " + type);
+        }
+    }
+
+    public static ImageChannelType encodeImageChannelType(int type) {
+        switch (type) {
+            case CL10.CL_FLOAT:
+                return ImageChannelType.FLOAT;
+            case CL10.CL_HALF_FLOAT:
+                return ImageChannelType.HALF_FLOAT;
+            case CL10.CL_SIGNED_INT16:
+                return ImageChannelType.SIGNED_INT16;
+            case CL10.CL_SIGNED_INT32:
+                return ImageChannelType.SIGNED_INT32;
+            case CL10.CL_SIGNED_INT8:
+                return ImageChannelType.SIGNED_INT8;
+            case CL10.CL_SNORM_INT16:
+                return ImageChannelType.SNORM_INT16;
+            case CL10.CL_SNORM_INT8:
+                return ImageChannelType.SNORM_INT8;
+            case CL10.CL_UNORM_INT8:
+                return ImageChannelType.UNORM_INT8;
+            case CL10.CL_UNORM_INT16:
+                return ImageChannelType.UNORM_INT16;
+            case CL10.CL_UNORM_INT_101010:
+                return ImageChannelType.UNORM_INT_101010;
+            case CL10.CL_UNORM_SHORT_555:
+                return ImageChannelType.UNORM_SHORT_555;
+            case CL10.CL_UNORM_SHORT_565:
+                return ImageChannelType.UNORM_SHORT_565;
+            case CL10.CL_UNSIGNED_INT16:
+                return ImageChannelType.UNSIGNED_INT16;
+            case CL10.CL_UNSIGNED_INT32:
+                return ImageChannelType.UNSIGNED_INT32;
+            case CL10.CL_UNSIGNED_INT8:
+                return ImageChannelType.UNSIGNED_INT8;
+            default:
+                //throw new com.jme3.opencl.OpenCLException("unknown image channel type id: " + type);
+                LOG.log(Level.WARNING, "Unknown image channel type id: {0}", type);
+                return null;
+        }
+    }
+
+    public static int decodeImageType(ImageType type) {
+        switch (type) {
+            case IMAGE_1D:
+                return CL12.CL_MEM_OBJECT_IMAGE1D;
+            case IMAGE_1D_ARRAY:
+                return CL12.CL_MEM_OBJECT_IMAGE1D_ARRAY;
+            case IMAGE_1D_BUFFER:
+                return CL12.CL_MEM_OBJECT_IMAGE1D_BUFFER;
+            case IMAGE_2D:
+                return CL10.CL_MEM_OBJECT_IMAGE2D;
+            case IMAGE_2D_ARRAY:
+                return CL12.CL_MEM_OBJECT_IMAGE2D_ARRAY;
+            case IMAGE_3D:
+                return CL10.CL_MEM_OBJECT_IMAGE3D;
+            default:
+                throw new IllegalArgumentException("Unknown image type: " + type);
+        }
+    }
+
+    public static ImageType encodeImageType(int type) {
+        switch (type) {
+            case CL12.CL_MEM_OBJECT_IMAGE1D:
+                return ImageType.IMAGE_1D;
+            case CL12.CL_MEM_OBJECT_IMAGE1D_ARRAY:
+                return ImageType.IMAGE_1D_ARRAY;
+            case CL12.CL_MEM_OBJECT_IMAGE1D_BUFFER:
+                return ImageType.IMAGE_1D_BUFFER;
+            case CL10.CL_MEM_OBJECT_IMAGE2D:
+                return ImageType.IMAGE_2D;
+            case CL12.CL_MEM_OBJECT_IMAGE2D_ARRAY:
+                return ImageType.IMAGE_2D_ARRAY;
+            case CL10.CL_MEM_OBJECT_IMAGE3D:
+                return ImageType.IMAGE_3D;
+            default:
+                throw new com.jme3.opencl.OpenCLException("Unknown image type id: " + type);
+        }
+    }
+
+    @Override
+    public long getWidth() {
+        return image.getImageInfoSize(CL10.CL_IMAGE_WIDTH);
+    }
+
+    @Override
+    public long getHeight() {
+        return image.getImageInfoSize(CL10.CL_IMAGE_HEIGHT);
+    }
+
+    @Override
+    public long getDepth() {
+        return image.getImageInfoSize(CL10.CL_IMAGE_DEPTH);
+    }
+
+    @Override
+    public long getRowPitch() {
+        return image.getImageInfoSize(CL10.CL_IMAGE_ROW_PITCH);
+    }
+
+    @Override
+    public long getSlicePitch() {
+        return image.getImageInfoSize(CL10.CL_IMAGE_SLICE_PITCH);
+    }
+
+    @Override
+    public long getArraySize() {
+        return image.getImageInfoSize(CL12.CL_IMAGE_ARRAY_SIZE);
+    }
+
+    @Override
+    public ImageFormat getImageFormat() {
+        CLImageFormat format = image.getImageFormat();
+        return new ImageFormat(
+                encodeImageChannelOrder(format.getChannelOrder()), 
+                encodeImageChannelType(format.getChannelType()));
+    }
+
+    @Override
+    public ImageType getImageType() {
+        int type = image.getInfoInt(CL10.CL_MEM_TYPE);
+        return encodeImageType(type);
+    }
+
+    @Override
+    public int getElementSize() {
+        return (int) image.getImageInfoSize(CL10.CL_IMAGE_ELEMENT_SIZE);
+    }
+
+    @Override
+    public void readImage(CommandQueue queue, ByteBuffer dest, long[] origin, long[] region, long rowPitch, long slicePitch) {
+        if (origin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[2].rewind();
+        Utils.pointerBuffers[1].put(origin).position(0);
+        Utils.pointerBuffers[2].put(region).position(0);
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10.clEnqueueReadImage(q, image, CL10.CL_TRUE, 
+                Utils.pointerBuffers[1], Utils.pointerBuffers[2], 
+                rowPitch, slicePitch, dest, null, null);
+        Utils.checkError(ret, "clEnqueueReadImage");
+    }
+
+    @Override
+    public Event readImageAsync(CommandQueue queue, ByteBuffer dest, long[] origin, long[] region, long rowPitch, long slicePitch) {
+        if (origin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointerBuffers[0].rewind();
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[2].rewind();
+        Utils.pointerBuffers[1].put(origin).position(0);
+        Utils.pointerBuffers[2].put(region).position(0);
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10.clEnqueueReadImage(q, image, CL10.CL_FALSE, 
+                Utils.pointerBuffers[1], Utils.pointerBuffers[2], 
+                rowPitch, slicePitch, dest, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueReadImage");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(q.getCLEvent(event));
+    }
+
+    @Override
+    public void writeImage(CommandQueue queue, ByteBuffer dest, long[] origin, long[] region, long rowPitch, long slicePitch) {
+        if (origin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[2].rewind();
+        Utils.pointerBuffers[1].put(origin).position(0);
+        Utils.pointerBuffers[2].put(region).position(0);
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10.clEnqueueWriteImage(q, image, CL10.CL_TRUE, 
+                Utils.pointerBuffers[1], Utils.pointerBuffers[2], 
+                rowPitch, slicePitch, dest, null, null);
+        Utils.checkError(ret, "clEnqueueWriteImage");
+    }
+
+    @Override
+    public Event writeImageAsync(CommandQueue queue, ByteBuffer dest, long[] origin, long[] region, long rowPitch, long slicePitch) {
+        if (origin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointerBuffers[0].rewind();
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[2].rewind();
+        Utils.pointerBuffers[1].put(origin).position(0);
+        Utils.pointerBuffers[2].put(region).position(0);
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10.clEnqueueWriteImage(q, image, CL10.CL_FALSE, 
+                Utils.pointerBuffers[1], Utils.pointerBuffers[2], 
+                rowPitch, slicePitch, dest, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueWriteImage");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(q.getCLEvent(event));
+    }
+
+    @Override
+    public void copyTo(CommandQueue queue, Image dest, long[] srcOrigin, long[] destOrigin, long[] region) {
+        if (srcOrigin.length!=3 || destOrigin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointerBuffers[0].rewind();
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[2].rewind();
+        Utils.pointerBuffers[3].rewind();
+        Utils.pointerBuffers[1].put(srcOrigin).position(0);
+        Utils.pointerBuffers[2].put(destOrigin).position(0);
+        Utils.pointerBuffers[3].put(region).position(0);
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10.clEnqueueCopyImage(q, image, ((LwjglImage) dest).getImage(), 
+                Utils.pointerBuffers[1], Utils.pointerBuffers[2], Utils.pointerBuffers[3], 
+                null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueCopyImage");
+        long event = Utils.pointerBuffers[0].get(0);
+        ret = CL10.clWaitForEvents(q.getCLEvent(event));
+        Utils.checkError(ret, "clWaitForEvents");
+    }
+
+    @Override
+    public Event copyToAsync(CommandQueue queue, Image dest, long[] srcOrigin, long[] destOrigin, long[] region) {
+        if (srcOrigin.length!=3 || destOrigin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointerBuffers[0].rewind();
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[2].rewind();
+        Utils.pointerBuffers[3].rewind();
+        Utils.pointerBuffers[1].put(srcOrigin).position(0);
+        Utils.pointerBuffers[2].put(destOrigin).position(0);
+        Utils.pointerBuffers[3].put(region).position(0);
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10.clEnqueueCopyImage(q, image, ((LwjglImage) dest).getImage(), 
+                Utils.pointerBuffers[1], Utils.pointerBuffers[2], Utils.pointerBuffers[3], 
+                null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueCopyImage");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(q.getCLEvent(event));
+    }
+
+    @Override
+    public ImageMapping map(CommandQueue queue, long[] origin, long[] region, MappingAccess access) {
+        if (origin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[2].rewind();
+        Utils.pointerBuffers[3].rewind();
+        Utils.pointerBuffers[4].rewind();
+        Utils.pointerBuffers[1].put(origin).position(0);
+        Utils.pointerBuffers[2].put(region).position(0);
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        long flags = Utils.getMappingAccessFlags(access);
+        Utils.errorBuffer.rewind();
+        ByteBuffer buf = CL10.clEnqueueMapImage(q, image, CL10.CL_TRUE, flags, 
+                Utils.pointerBuffers[1], Utils.pointerBuffers[2], 
+                Utils.pointerBuffers[3], Utils.pointerBuffers[4], null, null, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clEnqueueMapBuffer");
+        return new ImageMapping(buf, Utils.pointerBuffers[3].get(0), Utils.pointerBuffers[4].get(0));
+    }
+
+    @Override
+    public ImageMapping mapAsync(CommandQueue queue, long[] origin, long[] region, MappingAccess access) {
+        if (origin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointerBuffers[0].rewind();
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[2].rewind();
+        Utils.pointerBuffers[3].rewind();
+        Utils.pointerBuffers[4].rewind();
+        Utils.pointerBuffers[1].put(origin).position(0);
+        Utils.pointerBuffers[2].put(region).position(0);
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        long flags = Utils.getMappingAccessFlags(access);
+        Utils.errorBuffer.rewind();
+        ByteBuffer buf = CL10.clEnqueueMapImage(q, image, CL10.CL_FALSE, flags, 
+                Utils.pointerBuffers[1], Utils.pointerBuffers[2], 
+                Utils.pointerBuffers[3], Utils.pointerBuffers[4], null, Utils.pointerBuffers[0], Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clEnqueueMapBuffer");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new ImageMapping(buf, Utils.pointerBuffers[3].get(0), Utils.pointerBuffers[4].get(0), new LwjglEvent(q.getCLEvent(event)));
+    }
+
+    @Override
+    public void unmap(CommandQueue queue, ImageMapping mapping) {
+        mapping.buffer.position(0);
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        Utils.pointerBuffers[0].rewind();
+        int ret = CL10.clEnqueueUnmapMemObject(q, image, mapping.buffer, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueUnmapMemObject");
+        long event = Utils.pointerBuffers[0].get(0);
+        ret = CL10.clWaitForEvents(q.getCLEvent(event));
+        Utils.checkError(ret, "clWaitForEvents");
+    }
+
+    @Override
+    public Event fillAsync(CommandQueue queue, long[] origin, long[] region, ColorRGBA color) {
+        if (origin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointerBuffers[0].rewind();
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[2].rewind();
+        Utils.pointerBuffers[1].put(origin).position(0);
+        Utils.pointerBuffers[2].put(region).position(0);
+        Utils.tempBuffers[0].b16f.rewind();
+        Utils.tempBuffers[0].b16f.limit(4);
+        Utils.tempBuffers[0].b16f.put(color.r).put(color.g).put(color.b).put(color.a);
+        Utils.tempBuffers[0].b16.rewind();
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL12.clEnqueueFillImage(q, image, Utils.tempBuffers[0].b16, 
+                Utils.pointerBuffers[1], Utils.pointerBuffers[2], null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueFillImage");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(q.getCLEvent(event));
+        //TODO: why does q.getCLEvent(event) return null?
+        //This is a bug in LWJGL: they forgot to include the line
+        //  if ( __result == CL_SUCCESS ) command_queue.registerCLEvent(event);
+        // after the native call
+    }
+
+    @Override
+    public Event fillAsync(CommandQueue queue, long[] origin, long[] region, int[] color) {
+        if (color.length != 4) {
+            throw new IllegalArgumentException("the passed color array must have length 4");
+        }
+        if (origin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointerBuffers[0].rewind();
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[2].rewind();
+        Utils.pointerBuffers[1].put(origin).position(0);
+        Utils.pointerBuffers[2].put(region).position(0);
+        Utils.tempBuffers[0].b16i.rewind();
+        Utils.tempBuffers[0].b16i.limit(4);
+        Utils.tempBuffers[0].b16i.put(color);
+        Utils.tempBuffers[0].b16.rewind();
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL12.clEnqueueFillImage(q, image, Utils.tempBuffers[0].b16, 
+                Utils.pointerBuffers[1], Utils.pointerBuffers[2], null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueFillImage");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(q.getCLEvent(event));
+    }
+
+    @Override
+    public Event copyToBufferAsync(CommandQueue queue, Buffer dest, long[] srcOrigin, long[] srcRegion, long destOffset) {
+        if (srcOrigin.length!=3 || srcRegion.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointerBuffers[0].rewind();
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[2].rewind();
+        Utils.pointerBuffers[1].put(srcOrigin).position(0);
+        Utils.pointerBuffers[2].put(srcRegion).position(0);
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10.clEnqueueCopyImageToBuffer(q, image, ((LwjglBuffer) dest).getBuffer(), 
+                Utils.pointerBuffers[1], Utils.pointerBuffers[2], destOffset, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueCopyImageToBuffer");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(q.getCLEvent(event));
+    }
+
+    @Override
+    public Event acquireImageForSharingAsync(CommandQueue queue) {
+        Utils.pointerBuffers[0].rewind();
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10GL.clEnqueueAcquireGLObjects(q, image, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueAcquireGLObjects");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(q.getCLEvent(event));
+    }
+    @Override
+    public void acquireImageForSharingNoEvent(CommandQueue queue) {
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10GL.clEnqueueAcquireGLObjects(q, image, null, null);
+        Utils.checkError(ret, "clEnqueueAcquireGLObjects");
+    }
+    @Override
+    public Event releaseImageForSharingAsync(CommandQueue queue) {
+        Utils.pointerBuffers[0].rewind();
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10GL.clEnqueueReleaseGLObjects(q, image, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueReleaseGLObjects");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(q.getCLEvent(event));
+    }
+    @Override
+    public void releaseImageForSharingNoEvent(CommandQueue queue) {
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10GL.clEnqueueReleaseGLObjects(q, image, null, null);
+        Utils.checkError(ret, "clEnqueueReleaseGLObjects");
+    }
+    
+    private static class ReleaserImpl implements ObjectReleaser {
+        private CLMem mem;
+        private ReleaserImpl(CLMem mem) {
+            this.mem = mem;
+        }
+        @Override
+        public void release() {
+            if (mem != null) {
+                int ret = CL10.clReleaseMemObject(mem);
+                mem = null;
+                Utils.reportError(ret, "clReleaseMemObject");
+            }
+        }
+        
+    }
+}

+ 277 - 0
jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglKernel.java

@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.lwjgl;
+
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector4f;
+import com.jme3.opencl.*;
+import com.jme3.opencl.Buffer;
+import java.nio.*;
+import org.lwjgl.PointerBuffer;
+import org.lwjgl.opencl.CL10;
+import org.lwjgl.opencl.CLCommandQueue;
+import org.lwjgl.opencl.CLDevice;
+import org.lwjgl.opencl.CLKernel;
+
+/**
+ *
+ * @author shaman
+ */
+public class LwjglKernel extends Kernel {
+
+    private final CLKernel kernel;
+
+    public LwjglKernel(CLKernel kernel) {
+        super(new ReleaserImpl(kernel));
+        this.kernel = kernel;
+    }
+
+    public CLKernel getKernel() {
+        return kernel;
+    }
+    
+    @Override
+    public String getName() {
+        return kernel.getInfoString(CL10.CL_KERNEL_FUNCTION_NAME);
+    }
+
+    @Override
+    public int getArgCount() {
+        return kernel.getInfoInt(CL10.CL_KERNEL_NUM_ARGS);
+    }
+
+    @Override
+    public long getMaxWorkGroupSize(Device device) {
+        CLDevice d = ((LwjglDevice) device).getDevice();
+        return kernel.getWorkGroupInfoSize(d, CL10.CL_KERNEL_WORK_GROUP_SIZE);
+    }
+    
+    @Override
+    public void setArg(int index, LocalMemPerElement t) {
+        int ret = CL10.clSetKernelArg (kernel, index, t.getSize() * workGroupSize.getSizes()[0] * workGroupSize.getSizes()[1] * workGroupSize.getSizes()[2]);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, LocalMem t) {
+        int ret = CL10.clSetKernelArg (kernel, index, t.getSize());
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, Buffer t) {
+        int ret = CL10.clSetKernelArg(kernel, index, ((LwjglBuffer) t).getBuffer());
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+    
+    @Override
+    public void setArg(int index, Image i) {
+        int ret = CL10.clSetKernelArg(kernel, index, ((LwjglImage) i).getImage());
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, byte b) {
+        ByteBuffer buf = Utils.tempBuffers[0].b16;
+        buf.position(0);
+        buf.limit(1);
+        buf.put(0, b);
+        int ret = CL10.clSetKernelArg(kernel, index, buf);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, short s) {
+        ShortBuffer buf = Utils.tempBuffers[0].b16s;
+        buf.position(0);
+        buf.limit(1);
+        buf.put(0, s);
+        int ret = CL10.clSetKernelArg(kernel, index, buf);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, int i) {
+        IntBuffer buf = Utils.tempBuffers[0].b16i;
+        buf.position(0);
+        buf.limit(1);
+        buf.put(0, i);
+        int ret = CL10.clSetKernelArg(kernel, index, buf);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, long l) {
+        LongBuffer buf = Utils.tempBuffers[0].b16l;
+        buf.position(0);
+        buf.limit(1);
+        buf.put(0, l);
+        int ret = CL10.clSetKernelArg(kernel, index, buf);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, float f) {
+        FloatBuffer buf = Utils.tempBuffers[0].b16f;
+        buf.position(0);
+        buf.limit(1);
+        buf.put(0, f);
+        int ret = CL10.clSetKernelArg(kernel, index, buf);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, double d) {
+        DoubleBuffer buf = Utils.tempBuffers[0].b16d;
+        buf.position(0);
+        buf.limit(1);
+        buf.put(0, d);
+        int ret = CL10.clSetKernelArg(kernel, index, buf);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, Vector2f v) {
+        FloatBuffer buf = Utils.tempBuffers[0].b16f;
+        buf.position(0);
+        buf.limit(2);
+        buf.put(0, v.x);
+        buf.put(1, v.y);
+        int ret = CL10.clSetKernelArg(kernel, index, buf);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, Vector4f v) {
+        FloatBuffer buf = Utils.tempBuffers[0].b16f;
+        buf.position(0);
+        buf.limit(4);
+        buf.put(0, v.x);
+        buf.put(1, v.y);
+        buf.put(2, v.z);
+        buf.put(3, v.w);
+        int ret = CL10.clSetKernelArg(kernel, index, buf);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, Quaternion q) {
+        FloatBuffer buf = Utils.tempBuffers[0].b16f;
+        buf.position(0);
+        buf.limit(4);
+        buf.put(0, q.getX());
+        buf.put(1, q.getY());
+        buf.put(2, q.getZ());
+        buf.put(3, q.getW());
+        int ret = CL10.clSetKernelArg(kernel, index, buf);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+    
+    @Override
+    public void setArg(int index, Matrix4f m) {
+        FloatBuffer buf = Utils.b80f;
+        buf.position(0);
+        buf.limit(16);
+        buf.put(m.m00).put(m.m01).put(m.m02).put(m.m03);
+        buf.put(m.m10).put(m.m11).put(m.m12).put(m.m13);
+        buf.put(m.m20).put(m.m21).put(m.m22).put(m.m23);
+        buf.put(m.m30).put(m.m31).put(m.m32).put(m.m33);
+        buf.position(0);
+        int ret = CL10.clSetKernelArg(kernel, index, buf);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, ByteBuffer buffer, long size) {
+        buffer.limit((int) (buffer.position() + size));
+        int ret = CL10.clSetKernelArg(kernel, index, buffer);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public Event Run(CommandQueue queue) {
+        Utils.pointerBuffers[0].rewind();
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[1].put(globalWorkSize.getSizes());
+        Utils.pointerBuffers[1].position(0);
+        PointerBuffer p2 = null;
+        if (workGroupSize.getSizes()[0] > 0) {
+            p2 = Utils.pointerBuffers[2].rewind();
+            p2.put(workGroupSize.getSizes());
+            p2.position(0);
+        }
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10.clEnqueueNDRangeKernel(q, kernel,
+			globalWorkSize.getDimension(), null, Utils.pointerBuffers[1],
+			p2, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueNDRangeKernel");
+        return new LwjglEvent(q.getCLEvent(Utils.pointerBuffers[0].get(0)));
+    }
+    @Override
+    public void RunNoEvent(CommandQueue queue) {
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[1].put(globalWorkSize.getSizes());
+        Utils.pointerBuffers[1].position(0);
+        PointerBuffer p2 = null;
+        if (workGroupSize.getSizes()[0] > 0) {
+            p2 = Utils.pointerBuffers[2].rewind();
+            p2.put(workGroupSize.getSizes());
+            p2.position(0);
+        }
+        CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10.clEnqueueNDRangeKernel(q, kernel,
+			globalWorkSize.getDimension(), null, Utils.pointerBuffers[1],
+			p2, null, null);
+        Utils.checkError(ret, "clEnqueueNDRangeKernel");
+    }
+
+    @Override
+    public ObjectReleaser getReleaser() {
+        return new ReleaserImpl(kernel);
+    }
+    private static class ReleaserImpl implements ObjectReleaser {
+        private CLKernel kernel;
+        private ReleaserImpl(CLKernel kernel) {
+            this.kernel = kernel;
+        }
+        @Override
+        public void release() {
+            if (kernel != null) {
+                int ret = CL10.clReleaseKernel(kernel);
+                kernel = null;
+                Utils.reportError(ret, "clReleaseKernel");
+            }
+        }
+    }
+}

+ 127 - 0
jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglPlatform.java

@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.lwjgl;
+
+import com.jme3.opencl.Device;
+import com.jme3.opencl.Platform;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import org.lwjgl.opencl.CL10;
+import org.lwjgl.opencl.CLDevice;
+import org.lwjgl.opencl.CLPlatform;
+
+/**
+ *
+ * @author shaman
+ */
+public final class LwjglPlatform implements Platform {
+    
+    final CLPlatform platform;
+    List<LwjglDevice> devices;
+    
+    public LwjglPlatform(CLPlatform platform) {
+        this.platform = platform;
+    }
+
+    public CLPlatform getPlatform() {
+        return platform;
+    }
+    
+    @Override
+    public List<LwjglDevice> getDevices() {
+        if (devices == null) {
+            devices = new ArrayList<>();
+            for (CLDevice d : platform.getDevices(CL10.CL_DEVICE_TYPE_ALL)) {
+                devices.add(new LwjglDevice(d, this));
+            }
+        }
+        return devices;
+    }
+
+    @Override
+    public String getProfile() {
+        return platform.getInfoString(CL10.CL_PLATFORM_PROFILE);
+    }
+
+    @Override
+    public boolean isFullProfile() {
+        return getProfile().contains("FULL_PROFILE");
+    }
+
+    @Override
+    public boolean isEmbeddedProfile() {
+        return getProfile().contains("EMBEDDED_PROFILE");
+    }
+
+    @Override
+    public String getVersion() {
+        return platform.getInfoString(CL10.CL_PLATFORM_VERSION);
+    }
+
+    @Override
+    public int getVersionMajor() {
+        return Utils.getMajorVersion(getVersion(), "OpenCL ");
+    }
+
+    @Override
+    public int getVersionMinor() {
+        return Utils.getMinorVersion(getVersion(), "OpenCL ");
+    }
+
+    @Override
+    public String getName() {
+        return platform.getInfoString(CL10.CL_PLATFORM_NAME);
+    }
+
+    @Override
+    public String getVendor() {
+        return platform.getInfoString(CL10.CL_PLATFORM_VENDOR);
+    }
+
+    @Override
+    public boolean hasExtension(String extension) {
+        return getExtensions().contains(extension);
+    }
+
+    @Override
+    public boolean hasOpenGLInterop() {
+        return hasExtension("cl_khr_gl_sharing");
+    }
+
+    @Override
+    public Collection<? extends String> getExtensions() {
+        return Arrays.asList(platform.getInfoString(CL10.CL_PLATFORM_EXTENSIONS).split(" "));
+    }
+
+}

+ 145 - 0
jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglProgram.java

@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.lwjgl;
+
+import com.jme3.opencl.*;
+import java.nio.ByteBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.lwjgl.PointerBuffer;
+import org.lwjgl.opencl.*;
+
+/**
+ *
+ * @author shaman
+ */
+public class LwjglProgram extends Program {
+    private static final Logger LOG = Logger.getLogger(LwjglProgram.class.getName());
+    
+    private final CLProgram program;
+    private final LwjglContext context;
+
+    public LwjglProgram(CLProgram program, LwjglContext context) {
+        super(new ReleaserImpl(program));
+        this.program = program;
+        this.context = context;
+    }
+
+    public CLProgram getProgram() {
+        return program;
+    }
+
+    @Override
+    public void build(String args, Device... devices) throws KernelCompilationException {
+        PointerBuffer deviceList = null;
+        if (devices != null) {
+            deviceList = PointerBuffer.allocateDirect(devices.length);
+            deviceList.rewind();
+            for (Device d : devices) {
+                deviceList.put(((LwjglDevice) d).device.getPointer());
+            }
+            deviceList.flip();
+        }
+        int ret = CL10.clBuildProgram(program, deviceList, args, null);
+        if (ret != CL10.CL_SUCCESS) {
+            String log = Log();
+            LOG.log(Level.WARNING, "Unable to compile program:\n{0}", log);
+            if (ret == CL10.CL_BUILD_PROGRAM_FAILURE) {
+                throw new KernelCompilationException("Failed to build program", ret, log);
+            } else {
+                Utils.checkError(ret, "clBuildProgram");
+            }
+        } else {
+            LOG.log(Level.INFO, "Program compiled:\n{0}", Log());
+        }
+    }
+    
+    private String Log() {
+        StringBuilder str = new StringBuilder();
+        for (LwjglDevice device : context.getDevices()) {
+            CLDevice d = device.getDevice();
+            str.append(device.getName()).append(":\n");
+            str.append(program.getBuildInfoString(d, CL10.CL_PROGRAM_BUILD_LOG));
+            str.append('\n');
+        }
+        return str.toString();
+    }
+
+    @Override
+    public Kernel createKernel(String name) {
+        CLKernel kernel = CL10.clCreateKernel(program, name, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateKernel");
+        return new LwjglKernel(kernel);
+    }
+
+    @Override
+    public Kernel[] createAllKernels() {
+        CLKernel[] kernels = program.createKernelsInProgram();
+        Kernel[] kx = new Kernel[kernels.length];
+        for (int i=0; i<kernels.length; ++i) {
+            kx[i] = new LwjglKernel(kernels[i]);
+        }
+        return kx;
+    }
+
+    @Override
+    public ByteBuffer getBinary(Device device) {
+        ByteBuffer[] binaries = program.getInfoBinaries((ByteBuffer[]) null);
+        CLDevice[] devices = program.getInfoDevices();
+        //find the requested one
+        assert (binaries.length == devices.length);
+        for (int i=0; i<devices.length; ++i) {
+            if (((LwjglDevice) device).device == devices[i]) {
+                return binaries[i];
+            }
+        }
+        throw new com.jme3.opencl.OpenCLException("Program was not built against the specified device "+device);
+    }
+
+    private static class ReleaserImpl implements ObjectReleaser {
+        private CLProgram program;
+        private ReleaserImpl(CLProgram program) {
+            this.program = program;
+        }
+        @Override
+        public void release() {
+            //LWJGL Bug: releasing a program also released every! kernel associated with that program
+            /*
+            if (program != null) {
+                int ret = CL10.clReleaseProgram(program);
+                program = null;
+                Utils.reportError(ret, "clReleaseProgram");
+            }
+            */
+        }
+    }
+}

+ 167 - 0
jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/Utils.java

@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.lwjgl;
+
+import com.jme3.opencl.MappingAccess;
+import com.jme3.opencl.MemoryAccess;
+import com.jme3.opencl.OpenCLException;
+import java.lang.reflect.Field;
+import java.nio.*;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.lwjgl.BufferUtils;
+import org.lwjgl.LWJGLUtil;
+import org.lwjgl.PointerBuffer;
+import org.lwjgl.opencl.*;
+
+/**
+ *
+ * @author shaman
+ */
+public class Utils {
+    private static final Logger LOG = Logger.getLogger(Utils.class.getName());
+    private Utils() {}
+    
+    /** Maps OpenCL error token values to their String representations. 
+        Taken directly from org.lwjgl.opencl.Util
+     */
+	private static final Map<Integer, String> CL_ERROR_TOKENS = LWJGLUtil.getClassTokens(new LWJGLUtil.TokenFilter() {
+		public boolean accept(final Field field, final int value) {
+			return value < 0; // Currently, all OpenCL errors have negative values.
+		}
+	}, null, CL10.class, CL11.class, CL12.class, KHRGLSharing.class, KHRICD.class, APPLEGLSharing.class, EXTDeviceFission.class);
+    
+    public static int getMajorVersion(String version, String prefix) {
+        String s = version.substring(prefix.length());
+        return Integer.parseInt(s);
+    }
+    
+    public static int getMinorVersion(String version, String prefix) {
+        String s = version.substring(prefix.length());
+        int major = Integer.parseInt(s);
+        s = s.substring((int) (Math.log10(major) + 2));
+        return Integer.parseInt(s);
+    }
+    
+    public static final class TempBuffer {
+        public final ByteBuffer b16;
+        public final ShortBuffer b16s;
+        public final IntBuffer b16i;
+        public final LongBuffer b16l;
+        public final FloatBuffer b16f;
+        public final DoubleBuffer b16d;
+        public TempBuffer() {
+            b16 = BufferUtils.createByteBuffer(16);
+            b16s = b16.asShortBuffer();
+            b16i = b16.asIntBuffer();
+            b16l = b16.asLongBuffer();
+            b16f = b16.asFloatBuffer();
+            b16d = b16.asDoubleBuffer();
+        }
+    }
+    public static final ByteBuffer b80; //needed for ImageDescriptor and Matrix4f
+    public static final LongBuffer b80l;
+    public static final FloatBuffer b80f;
+    public static final TempBuffer[] tempBuffers = new TempBuffer[8];
+    public static final PointerBuffer[] pointerBuffers = new PointerBuffer[8];
+    static {
+        for (int i=0; i<8; ++i) {
+            tempBuffers[i] = new TempBuffer();
+            pointerBuffers[i] = PointerBuffer.allocateDirect(4);
+        }
+        errorBuffer = BufferUtils.createIntBuffer(1);
+        b80 = BufferUtils.createByteBuffer(80);
+        b80l = b80.asLongBuffer();
+        b80f = b80.asFloatBuffer();
+    }
+    
+    public static IntBuffer errorBuffer;
+    public static void checkError(IntBuffer errorBuffer, String callName) {
+        checkError(errorBuffer.get(0), callName);
+    }
+    public static void checkError(int error, String callName) {
+        if (error != CL10.CL_SUCCESS) {
+            String errname = getErrorName(error);
+            if (errname == null) {
+                errname = "UNKNOWN";
+            }
+            throw new OpenCLException("OpenCL error in " + callName + ": " + errname + " (0x" + Integer.toHexString(error) + ")", error);
+        }
+    }
+    
+    public static void reportError(int error, String callName) {
+        if (error != CL10.CL_SUCCESS) {
+            String errname = getErrorName(error);
+            if (errname == null) {
+                errname = "UNKNOWN";
+            }
+            LOG.log(Level.WARNING, "OpenCL error in {0}: {1} (0x{2})", new Object[]{callName, errname, Integer.toHexString(error)});
+        }
+    }
+    
+    public static String getErrorName(int code) {
+        return CL_ERROR_TOKENS.get(code);
+    }
+    
+    public static long getMemoryAccessFlags(MemoryAccess ma) {
+        switch (ma) {
+            case READ_ONLY: return CL10.CL_MEM_READ_ONLY;
+            case WRITE_ONLY: return CL10.CL_MEM_WRITE_ONLY;
+            case READ_WRITE: return CL10.CL_MEM_READ_WRITE;
+            default: throw new IllegalArgumentException("Unknown memory access: "+ma);
+        }
+    }
+    public static MemoryAccess getMemoryAccessFromFlag(long flag) {
+        if ((flag & CL10.CL_MEM_READ_WRITE) > 0) {
+            return MemoryAccess.READ_WRITE;
+        }
+        if ((flag & CL10.CL_MEM_READ_ONLY) > 0) {
+            return MemoryAccess.READ_ONLY;
+        }
+        if ((flag & CL10.CL_MEM_WRITE_ONLY) > 0) {
+            return MemoryAccess.WRITE_ONLY;
+        }
+        throw new OpenCLException("Unknown memory access flag: "+flag);
+    }
+    
+    public static long getMappingAccessFlags(MappingAccess ma) {
+        switch (ma) {
+            case MAP_READ_ONLY: return CL10.CL_MAP_READ;
+            case MAP_READ_WRITE: return CL10.CL_MAP_READ | CL10.CL_MAP_WRITE;
+            case MAP_WRITE_ONLY: return CL10.CL_MAP_WRITE;
+            case MAP_WRITE_INVALIDATE: return CL12.CL_MAP_WRITE_INVALIDATE_REGION;
+            default: throw new IllegalArgumentException("Unknown mapping access: "+ma);
+        }
+    }
+
+}

+ 115 - 0
jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java

@@ -35,6 +35,11 @@ package com.jme3.system.lwjgl;
 import com.jme3.input.lwjgl.JInputJoyInput;
 import com.jme3.input.lwjgl.LwjglKeyInput;
 import com.jme3.input.lwjgl.LwjglMouseInput;
+import com.jme3.opencl.Device;
+import com.jme3.opencl.PlatformChooser;
+import com.jme3.opencl.lwjgl.LwjglDevice;
+import com.jme3.opencl.lwjgl.LwjglPlatform;
+import com.jme3.opencl.DefaultPlatformChooser;
 import com.jme3.renderer.Renderer;
 import com.jme3.renderer.RendererException;
 import com.jme3.renderer.lwjgl.LwjglGL;
@@ -54,12 +59,15 @@ import com.jme3.renderer.opengl.GLTimingState;
 import com.jme3.renderer.opengl.GLTracer;
 import com.jme3.system.*;
 import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
 
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import org.lwjgl.LWJGLException;
 import org.lwjgl.Sys;
+import org.lwjgl.opencl.*;
 import org.lwjgl.opengl.*;
 
 /**
@@ -81,6 +89,9 @@ public abstract class LwjglContext implements JmeContext {
     protected JInputJoyInput joyInput;
     protected Timer timer;
     protected SystemListener listener;
+    
+    protected LwjglPlatform clPlatform;
+    protected com.jme3.opencl.lwjgl.LwjglContext clContext;
 
     public void setSystemListener(SystemListener listener) {
         this.listener = listener;
@@ -245,8 +256,108 @@ public abstract class LwjglContext implements JmeContext {
         if (joyInput != null) {
             joyInput.initialize();
         }
+        
     }
 
+    @SuppressWarnings("unchecked")
+    protected void initOpenCL() {
+        logger.info("Initialize OpenCL wiht LWJGL2");
+        
+        try {
+            CL.create();
+        } catch (LWJGLException ex) {
+            logger.log(Level.SEVERE, "Unable to initialize OpenCL", ex);
+            return;
+        }
+        
+        //load platforms and devices
+        StringBuilder platformInfos = new StringBuilder();
+        ArrayList<LwjglPlatform> platforms = new ArrayList<>();
+        for (CLPlatform p : CLPlatform.getPlatforms()) {
+            platforms.add(new LwjglPlatform(p));
+        }
+        platformInfos.append("Available OpenCL platforms:");
+        for (int i=0; i<platforms.size(); ++i) {
+            LwjglPlatform platform = platforms.get(i);
+            platformInfos.append("\n * Platform ").append(i+1);
+            platformInfos.append("\n *   Name: ").append(platform.getName());
+            platformInfos.append("\n *   Vendor: ").append(platform.getVendor());
+            platformInfos.append("\n *   Version: ").append(platform.getVersion());
+            platformInfos.append("\n *   Profile: ").append(platform.getProfile());
+            platformInfos.append("\n *   Supports interop: ").append(platform.hasOpenGLInterop());
+            List<LwjglDevice> devices = platform.getDevices();
+            platformInfos.append("\n *   Available devices:");
+            for (int j=0; j<devices.size(); ++j) {
+                LwjglDevice device = devices.get(j);
+                platformInfos.append("\n *    * Device ").append(j+1);
+                platformInfos.append("\n *    *   Name: ").append(device.getName());
+                platformInfos.append("\n *    *   Vendor: ").append(device.getVendor());
+                platformInfos.append("\n *    *   Version: ").append(device.getVersion());
+                platformInfos.append("\n *    *   Profile: ").append(device.getProfile());
+                platformInfos.append("\n *    *   Compiler version: ").append(device.getCompilerVersion());
+                platformInfos.append("\n *    *   Device type: ").append(device.getDeviceType());
+                platformInfos.append("\n *    *   Compute units: ").append(device.getComputeUnits());
+                platformInfos.append("\n *    *   Work group size: ").append(device.getMaxiumWorkItemsPerGroup());
+                platformInfos.append("\n *    *   Global memory: ").append(device.getGlobalMemorySize()).append("B");
+                platformInfos.append("\n *    *   Local memory: ").append(device.getLocalMemorySize()).append("B");
+                platformInfos.append("\n *    *   Constant memory: ").append(device.getMaximumConstantBufferSize()).append("B");
+                platformInfos.append("\n *    *   Supports double: ").append(device.hasDouble());
+                platformInfos.append("\n *    *   Supports half floats: ").append(device.hasHalfFloat());
+                platformInfos.append("\n *    *   Supports writable 3d images: ").append(device.hasWritableImage3D());
+                platformInfos.append("\n *    *   Supports interop: ").append(device.hasOpenGLInterop());
+            }
+        }
+        logger.info(platformInfos.toString());
+        
+        //choose devices
+        PlatformChooser chooser = null;
+        if (settings.getOpenCLPlatformChooser() != null) {
+            try {
+                chooser = (PlatformChooser) Class.forName(settings.getOpenCLPlatformChooser()).newInstance();
+            } catch (Exception ex) {
+                logger.log(Level.WARNING, "unable to instantiate custom PlatformChooser", ex);
+            }
+        }
+        if (chooser == null) {
+            chooser = new DefaultPlatformChooser();
+        }
+        List<? extends Device> choosenDevices = chooser.chooseDevices(platforms);
+        List<CLDevice> devices = new ArrayList<>(choosenDevices.size());
+        LwjglPlatform platform = null;
+        for (Device d : choosenDevices) {
+            if (!(d instanceof LwjglDevice)) {
+                logger.log(Level.SEVERE, "attempt to return a custom Device implementation from PlatformChooser: {0}", d);
+                return;
+            }
+            LwjglDevice ld = (LwjglDevice) d;
+            if (platform == null) {
+                platform = ld.getPlatform();
+            } else if (platform != ld.getPlatform()) {
+                logger.severe("attempt to use devices from different platforms");
+                return;
+            }
+            devices.add(ld.getDevice());
+        }
+        if (devices.isEmpty()) {
+            logger.warning("no devices specified, no OpenCL context created");
+            return;
+        }
+        clPlatform = platform;
+        logger.log(Level.INFO, "chosen platform: {0}", platform.getName());
+        logger.log(Level.INFO, "chosen devices: {0}", choosenDevices);
+        
+        //create context
+        try {
+            CLContext c = CLContext.create(platform.getPlatform(), devices, null, Display.getDrawable(), null);
+            clContext = new com.jme3.opencl.lwjgl.LwjglContext(c, (List<LwjglDevice>) choosenDevices);
+        } catch (LWJGLException ex) {
+            logger.log(Level.SEVERE, "Unable to create OpenCL context", ex);
+            return;
+        }
+        
+        logger.info("OpenCL context created");
+    }
+    
     public void internalDestroy() {
         renderer = null;
         timer = null;
@@ -311,4 +422,8 @@ public abstract class LwjglContext implements JmeContext {
         return timer;
     }
 
+    @Override
+    public com.jme3.opencl.Context getOpenCLContext() {
+        return clContext;
+    }
 }

+ 4 - 0
jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglDisplay.java

@@ -149,6 +149,10 @@ public class LwjglDisplay extends LwjglAbstractDisplay {
                 GL11.glEnable(ARBMultisample.GL_MULTISAMPLE_ARB);
             }
         }
+        
+        if (settings.isOpenCLSupport()) {
+            initOpenCL();
+        }
     }
     
     protected void destroyContext(){

+ 238 - 0
jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglBuffer.java

@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.lwjgl;
+
+import com.jme3.opencl.*;
+import java.nio.ByteBuffer;
+import org.lwjgl.opencl.*;
+
+/**
+ *
+ * @author shaman
+ */
+public class LwjglBuffer extends Buffer {
+
+    private final long buffer;
+
+    public LwjglBuffer(long buffer) {
+        super(new ReleaserImpl(buffer));
+        this.buffer = buffer;
+    }
+    public long getBuffer() {
+        return buffer;
+    }
+    
+    @Override
+    public long getSize() {
+        return Info.clGetMemObjectInfoLong(buffer, CL10.CL_MEM_SIZE);
+    }
+
+    @Override
+    public MemoryAccess getMemoryAccessFlags() {
+        return Utils.getMemoryAccessFromFlag(Info.clGetMemObjectInfoLong(buffer, CL10.CL_MEM_FLAGS));
+    }
+
+    @Override
+    public void read(CommandQueue queue, ByteBuffer dest, long size, long offset) {
+        //Note: LWJGL does not support the size parameter, I have to set the buffer limit
+        dest.limit((int) (dest.position() + size));
+        int ret = CL10.clEnqueueReadBuffer(((LwjglCommandQueue)queue).getQueue(), 
+                buffer, CL10.CL_TRUE, offset, dest, null, null);
+        Utils.checkError(ret, "clEnqueueReadBuffer");
+    }
+
+    @Override
+    public Event readAsync(CommandQueue queue, ByteBuffer dest, long size, long offset) {
+        //Note: LWJGL does not support the size parameter, I have to set the buffer limit
+        dest.limit((int) (dest.position() + size));
+        Utils.pointerBuffers[0].rewind();
+        long q = ((LwjglCommandQueue)queue).getQueue();
+        int ret = CL10.clEnqueueReadBuffer(q, buffer, CL10.CL_FALSE, offset, dest, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueReadBuffer");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(event);
+    }
+
+    @Override
+    public void write(CommandQueue queue, ByteBuffer src, long size, long offset) {
+        //Note: LWJGL does not support the size parameter, I have to set the buffer limit
+        src.limit((int) (src.position() + size));
+        long q = ((LwjglCommandQueue)queue).getQueue();
+        int ret = CL10.clEnqueueWriteBuffer(q, buffer, CL10.CL_TRUE, offset, src, null, null);
+        Utils.checkError(ret, "clEnqueueWriteBuffer");
+    }
+
+    @Override
+    public Event writeAsync(CommandQueue queue, ByteBuffer src, long size, long offset) {
+        //Note: LWJGL does not support the size parameter, I have to set the buffer limit
+        src.limit((int) (src.position() + size));
+        Utils.pointerBuffers[0].rewind();
+        long q = ((LwjglCommandQueue)queue).getQueue();
+        int ret = CL10.clEnqueueWriteBuffer(q, buffer, CL10.CL_FALSE, offset, src, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueWriteBuffer");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(event);
+    }
+
+    @Override
+    public void copyTo(CommandQueue queue, Buffer dest, long size, long srcOffset, long destOffset) {
+        long q = ((LwjglCommandQueue)queue).getQueue();
+        Utils.pointerBuffers[0].rewind();
+        int ret = CL10.clEnqueueCopyBuffer(q, buffer, ((LwjglBuffer) dest).buffer, srcOffset, destOffset, size, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueCopyBuffer");
+        long event = Utils.pointerBuffers[0].get(0);
+        ret = CL10.clWaitForEvents(event);
+        Utils.checkError(ret, "clWaitForEvents");
+    }
+
+    @Override
+    public Event copyToAsync(CommandQueue queue, Buffer dest, long size, long srcOffset, long destOffset) {
+        long q = ((LwjglCommandQueue)queue).getQueue();
+        Utils.pointerBuffers[0].rewind();
+        int ret = CL10.clEnqueueCopyBuffer(q, buffer, ((LwjglBuffer) dest).buffer, srcOffset, destOffset, size, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueCopyBuffer");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(event);
+    }
+
+    @Override
+    public ByteBuffer map(CommandQueue queue, long size, long offset, MappingAccess access) {
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        long flags = Utils.getMappingAccessFlags(access);
+        Utils.errorBuffer.rewind();
+        ByteBuffer b = CL10.clEnqueueMapBuffer(q, buffer, CL10.CL_TRUE, flags, offset, size, null, null, Utils.errorBuffer, null);
+        Utils.checkError(Utils.errorBuffer, "clEnqueueMapBuffer");
+        return b;
+    }
+
+    @Override
+    public void unmap(CommandQueue queue, ByteBuffer ptr) {
+        ptr.position(0);
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        Utils.pointerBuffers[0].rewind();
+        int ret = CL10.clEnqueueUnmapMemObject(q, buffer, ptr, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueUnmapMemObject");
+        long event = Utils.pointerBuffers[0].get(0);
+        ret = CL10.clWaitForEvents(event);
+        Utils.checkError(ret, "clWaitForEvents");
+    }
+
+    @Override
+    public com.jme3.opencl.Buffer.AsyncMapping mapAsync(CommandQueue queue, long size, long offset, MappingAccess access) {
+        Utils.pointerBuffers[0].rewind();
+        Utils.errorBuffer.rewind();
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        long flags = Utils.getMappingAccessFlags(access);
+        ByteBuffer buf = CL10.clEnqueueMapBuffer(q, buffer, CL10.CL_FALSE, flags, offset, size, null, Utils.pointerBuffers[0], Utils.errorBuffer, null);
+        Utils.checkError(Utils.errorBuffer, "clEnqueueMapBuffer");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new com.jme3.opencl.Buffer.AsyncMapping(new LwjglEvent(event), buf);
+    }
+
+    @Override
+    public Event fillAsync(CommandQueue queue, ByteBuffer pattern, long size, long offset) {
+        Utils.pointerBuffers[0].rewind();
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL12.clEnqueueFillBuffer(q, buffer, pattern, offset, size, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueFillBuffer");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(event);
+    }
+
+    @Override
+    public Event copyToImageAsync(CommandQueue queue, Image dest, long srcOffset, long[] destOrigin, long[] destRegion) {
+        if (destOrigin.length!=3 || destRegion.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointerBuffers[0].rewind();
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[2].rewind();
+        Utils.pointerBuffers[1].put(destOrigin).position(0);
+        Utils.pointerBuffers[2].put(destRegion).position(0);
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10.clEnqueueCopyBufferToImage(q, buffer, ((LwjglImage) dest).getImage(), 
+                srcOffset, Utils.pointerBuffers[1], Utils.pointerBuffers[2], null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueCopyBufferToImage");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(event);
+    }
+
+    @Override
+    public Event acquireBufferForSharingAsync(CommandQueue queue) {
+        Utils.pointerBuffers[0].rewind();
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10GL.clEnqueueAcquireGLObjects(q, buffer, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueAcquireGLObjects");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(event);
+    }
+    @Override
+    public void acquireBufferForSharingNoEvent(CommandQueue queue) {
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10GL.clEnqueueAcquireGLObjects(q, buffer, null, null);
+        Utils.checkError(ret, "clEnqueueAcquireGLObjects");
+    }
+
+    @Override
+    public Event releaseBufferForSharingAsync(CommandQueue queue) {
+        Utils.assertSharingPossible();
+        Utils.pointerBuffers[0].rewind();
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10GL.clEnqueueReleaseGLObjects(q, buffer, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueReleaseGLObjects");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(event);
+    }
+    @Override
+    public void releaseBufferForSharingNoEvent(CommandQueue queue) {
+        Utils.assertSharingPossible();
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10GL.clEnqueueReleaseGLObjects(q, buffer, null, null);
+        Utils.checkError(ret, "clEnqueueReleaseGLObjects");
+    }
+
+    private static class ReleaserImpl implements ObjectReleaser {
+        private long mem;
+        private ReleaserImpl(long mem) {
+            this.mem = mem;
+        }
+        @Override
+        public void release() {
+            if (mem != 0) {
+                int ret = CL10.clReleaseMemObject(mem);
+                mem = 0;
+                Utils.reportError(ret, "clReleaseMemObject");
+            }
+        }
+        
+    }
+}

+ 80 - 0
jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglCommandQueue.java

@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.lwjgl;
+
+import com.jme3.opencl.CommandQueue;
+import org.lwjgl.opencl.CL10;
+
+/**
+ *
+ * @author shaman
+ */
+public class LwjglCommandQueue extends CommandQueue {
+
+    private final long queue;
+
+    public LwjglCommandQueue(long queue) {
+        super(new ReleaserImpl(queue));
+        this.queue = queue;
+    }
+    
+    public long getQueue() {
+        return queue;
+    }
+    
+    @Override
+    public void flush() {
+        int ret = CL10.clFlush(queue);
+        Utils.checkError(ret, "clFlush");
+    }
+
+    @Override
+    public void finish() {
+        int ret = CL10.clFinish(queue);
+        Utils.checkError(ret, "clFinish");
+    }
+    
+    private static class ReleaserImpl implements ObjectReleaser {
+        private long queue;
+        private ReleaserImpl(long queue) {
+            this.queue = queue;
+        }
+        @Override
+        public void release() {
+            if (queue != 0) {
+                int ret = CL10.clReleaseCommandQueue(queue);
+                queue = 0;
+                Utils.reportError(ret, "clReleaseCommandQueue");
+            }
+        }
+    }
+}

+ 255 - 0
jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglContext.java

@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.lwjgl;
+
+import com.jme3.opencl.*;
+import com.jme3.opencl.Context;
+import com.jme3.opencl.Image.ImageDescriptor;
+import com.jme3.opencl.Image.ImageFormat;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Texture;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.lwjgl.BufferUtils;
+import org.lwjgl.opencl.*;
+import org.lwjgl.opengl.*;
+
+/**
+ *
+ * @author shaman
+ */
+public class LwjglContext extends Context {
+    private static final Logger LOG = Logger.getLogger(LwjglContext.class.getName());
+    private final long context;
+    private final List<LwjglDevice> devices;
+
+    public LwjglContext(long context, List<LwjglDevice> devices) {
+        super(new ReleaserImpl(context, devices));
+        this.context = context;
+        this.devices = devices;
+    }
+
+    public long getContext() {
+        return context;
+    }
+
+    @Override
+    public List<LwjglDevice> getDevices() {
+        return devices;
+    }
+
+    @Override
+    @SuppressWarnings("element-type-mismatch")
+    public CommandQueue createQueue(Device device) {
+        assert (devices.contains(device)); //this also ensures that device is a LwjglDevice
+        long d = ((LwjglDevice) device).getDevice();
+        long properties = 0;
+        long q = CL10.clCreateCommandQueue(context, d, properties, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateCommandQueue");
+        return new LwjglCommandQueue(q);
+    }
+    
+    @Override
+    public Buffer createBuffer(long size, MemoryAccess access) {
+        long flags = Utils.getMemoryAccessFlags(access);
+        long mem = CL10.clCreateBuffer(context, flags, size, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateBuffer");
+        return new LwjglBuffer(mem);
+    }
+
+    @Override
+    public Buffer createBufferFromHost(ByteBuffer data, MemoryAccess access) {
+        long flags = Utils.getMemoryAccessFlags(access);
+        flags |= CL10.CL_MEM_USE_HOST_PTR;
+        long mem = CL10.clCreateBuffer(context, flags, data, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateBuffer");
+        return new LwjglBuffer(mem);
+    }
+
+    @Override
+    public Image createImage(MemoryAccess access, ImageFormat format, ImageDescriptor descr) {
+        long memFlags = Utils.getMemoryAccessFlags(access);
+        Utils.errorBuffer.rewind();
+
+        CLImageFormat f = null;
+        CLImageDesc d = null;
+        try {
+            f = CLImageFormat.malloc();
+            d = CLImageDesc.calloc();
+            f.image_channel_data_type(LwjglImage.decodeImageChannelType(format.channelType));
+            f.image_channel_order(LwjglImage.decodeImageChannelOrder(format.channelOrder));
+            d.image_type(LwjglImage.decodeImageType(descr.type));
+            d.image_width(descr.width);
+            d.image_height(descr.height);
+            d.image_depth(descr.depth);
+            d.image_array_size(descr.arraySize);
+            d.image_row_pitch(descr.rowPitch);
+            d.image_slice_pitch(descr.slicePitch);
+            //create image
+            long mem = CL12.clCreateImage(context, memFlags, f, d, descr.hostPtr, Utils.errorBuffer);
+            Utils.checkError(Utils.errorBuffer, "clCreateImage");
+            return new LwjglImage(mem);
+        } finally {
+            if (f != null) {
+                f.free();
+            }
+            if (d != null) {
+                d.free();
+            }
+        }
+    }
+
+    @Override
+    public ImageFormat[] querySupportedFormats(MemoryAccess access, Image.ImageType type) {
+        long memFlags = Utils.getMemoryAccessFlags(access);
+        int typeFlag = LwjglImage.decodeImageType(type);
+        Utils.tempBuffers[0].b16i.rewind();
+        //query count
+        int ret = CL10.clGetSupportedImageFormats(context, memFlags, typeFlag, null, Utils.tempBuffers[0].b16i);
+        Utils.checkError(ret, "clGetSupportedImageFormats");
+        int count = Utils.tempBuffers[0].b16i.get(0);
+        if (count == 0) {
+            return new ImageFormat[0];
+        }
+        //get formats
+        CLImageFormat.Buffer formatsB = new CLImageFormat.Buffer(BufferUtils.createByteBuffer(count * CLImageFormat.SIZEOF));
+        ret = CL10.clGetSupportedImageFormats(context, memFlags, typeFlag, formatsB, null);
+        Utils.checkError(ret, "clGetSupportedImageFormats");
+        //convert formats
+        ImageFormat[] formats = new ImageFormat[count];
+        for (int i=0; i<count; ++i) {
+            CLImageFormat f = formatsB.get();
+            Image.ImageChannelOrder channelOrder = LwjglImage.encodeImageChannelOrder(f.image_channel_order());
+            Image.ImageChannelType channelType = LwjglImage.encodeImageChannelType(f.image_channel_data_type());
+            formats[i] = new ImageFormat(channelOrder, channelType);
+        }
+        return formats;
+    }
+
+    @Override
+    public Buffer bindVertexBuffer(VertexBuffer vb, MemoryAccess access) {
+        Utils.assertSharingPossible();
+        int id = vb.getId();
+        if (id == -1) {
+            throw new IllegalArgumentException("vertex buffer was not yet uploaded to the GPU or is CPU only");
+        }
+        long flags = Utils.getMemoryAccessFlags(access);
+        Utils.errorBuffer.rewind();
+        long mem = CL10GL.clCreateFromGLBuffer(context, flags, id, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateFromGLBuffer");
+        return new LwjglBuffer(mem);
+    }
+
+    @Override
+    public Image bindImage(com.jme3.texture.Image image, Texture.Type textureType, int miplevel, MemoryAccess access) {
+        Utils.assertSharingPossible();
+        int imageID = image.getId();
+        if (imageID == -1) {
+            throw new IllegalArgumentException("image was not yet uploaded to the GPU");
+        }
+        long memFlags = Utils.getMemoryAccessFlags(access);
+        int textureTarget = convertTextureType(textureType);
+        Utils.errorBuffer.rewind();
+        long mem = CL12GL.clCreateFromGLTexture(context, memFlags, textureTarget, miplevel, imageID, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateFromGLTexture");
+        return new LwjglImage(mem);
+    }
+
+    @Override
+    protected Image bindPureRenderBuffer(FrameBuffer.RenderBuffer buffer, MemoryAccess access) {
+        Utils.assertSharingPossible();
+        int renderbuffer = buffer.getId();
+        if (renderbuffer == -1) {
+            throw new IllegalArgumentException("renderbuffer was not yet uploaded to the GPU");
+        }
+        long memFlags = Utils.getMemoryAccessFlags(access);
+        Utils.errorBuffer.rewind();
+        long mem = CL10GL.clCreateFromGLRenderbuffer(context, memFlags, renderbuffer, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateFromGLRenderbuffer");
+        return new LwjglImage(mem);
+    }
+    
+    private int convertTextureType(Texture.Type textureType) {
+        switch (textureType) {
+            case TwoDimensional: return GL11.GL_TEXTURE_2D;
+            case TwoDimensionalArray: return GL30.GL_TEXTURE_2D_ARRAY;
+            case ThreeDimensional: return GL12.GL_TEXTURE_3D;
+            case CubeMap: return GL13.GL_TEXTURE_CUBE_MAP;
+            default: throw new IllegalArgumentException("unknown texture type "+textureType);
+        }
+    }
+
+    @Override
+    public Program createProgramFromSourceCode(String sourceCode) {
+        LOG.log(Level.FINE, "Create program from source:\n{0}", sourceCode);
+        Utils.errorBuffer.rewind();
+        long p = CL10.clCreateProgramWithSource(context, sourceCode, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateProgramWithSource");
+        return new LwjglProgram(p, this);
+    }
+
+    @Override
+    public Program createProgramFromBinary(ByteBuffer binaries, Device device) {
+        Utils.errorBuffer.rewind();
+        Utils.tempBuffers[0].b16i.rewind();
+        Utils.pointerBuffers[0].rewind();
+        Utils.pointerBuffers[0].put(0, ((LwjglDevice) device).getDevice());
+        long p = CL10.clCreateProgramWithBinary(context, Utils.pointerBuffers[0], 
+                binaries, Utils.tempBuffers[0].b16i, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateProgramWithBinary");
+        Utils.checkError(Utils.tempBuffers[0].b16i, "clCreateProgramWithBinary");
+        return new LwjglProgram(p, this);
+    }
+
+    private static class ReleaserImpl implements ObjectReleaser {
+        private long context;
+        private final List<LwjglDevice> devices;
+        private ReleaserImpl(long mem, List<LwjglDevice> devices) {
+            this.context = mem;
+            this.devices = devices;
+        }
+        @Override
+        public void release() {
+            if (context != 0) {
+                int ret = CL10.clReleaseContext(context);
+                context = 0;
+                devices.clear();
+                Utils.reportError(ret, "clReleaseMemObject");
+            }
+        }
+        
+    }
+}

+ 303 - 0
jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglDevice.java

@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.lwjgl;
+
+import com.jme3.opencl.Device;
+import com.jme3.opencl.Platform;
+import java.util.Arrays;
+import java.util.Collection;
+import org.lwjgl.PointerBuffer;
+import org.lwjgl.opencl.CL10;
+import org.lwjgl.opencl.CL11;
+import org.lwjgl.opencl.CLDevice;
+import org.lwjgl.opencl.Info;
+
+/**
+ *
+ * @author shaman
+ */
+public final class LwjglDevice implements Device {
+
+    final CLDevice device;
+    final LwjglPlatform platform;
+
+    public LwjglDevice(CLDevice device, LwjglPlatform platform) {
+        this.device = device;
+        this.platform = platform;
+    }
+    
+    public long getDevice() {
+        return device.address();
+    }
+    public CLDevice getCLDevice() {
+        return device;
+    }
+    
+    @Override
+    public LwjglPlatform getPlatform() {
+        return platform;
+    }
+
+    @Override
+    public DeviceType getDeviceType() {
+        int type = Info.clGetDeviceInfoInt(device.address(), CL10.CL_DEVICE_TYPE);
+        switch (type) {
+            case CL10.CL_DEVICE_TYPE_ACCELERATOR: return DeviceType.ACCELEARTOR;
+            case CL10.CL_DEVICE_TYPE_CPU: return DeviceType.CPU;
+            case CL10.CL_DEVICE_TYPE_GPU: return DeviceType.GPU;
+            default: return DeviceType.DEFAULT;
+        }
+    }
+
+    @Override
+    public int getVendorId() {
+        return Info.clGetDeviceInfoInt(device.address(), CL10.CL_DEVICE_VENDOR_ID);
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return Info.clGetDeviceInfoBoolean(device.address(), CL10.CL_DEVICE_AVAILABLE);
+    }
+
+    @Override
+    public boolean hasCompiler() {
+        return Info.clGetDeviceInfoBoolean(device.address(), CL10.CL_DEVICE_COMPILER_AVAILABLE);
+    }
+
+    @Override
+    public boolean hasDouble() {
+        return hasExtension("cl_khr_fp64");
+    }
+
+    @Override
+    public boolean hasHalfFloat() {
+        return hasExtension("cl_khr_fp16");
+    }
+
+    @Override
+    public boolean hasErrorCorrectingMemory() {
+        return Info.clGetDeviceInfoBoolean(device.address(), CL10.CL_DEVICE_ERROR_CORRECTION_SUPPORT);
+    }
+
+    @Override
+    public boolean hasUnifiedMemory() {
+        return Info.clGetDeviceInfoBoolean(device.address(), CL11.CL_DEVICE_HOST_UNIFIED_MEMORY);
+    }
+
+    @Override
+    public boolean hasImageSupport() {
+        return Info.clGetDeviceInfoBoolean(device.address(), CL10.CL_DEVICE_IMAGE_SUPPORT);
+    }
+    
+    @Override
+    public boolean hasWritableImage3D() {
+        return hasExtension("cl_khr_3d_image_writes");
+    }
+
+    @Override
+    public boolean hasOpenGLInterop() {
+        return hasExtension("cl_khr_gl_sharing");
+    }
+    
+    @Override
+    public boolean hasExtension(String extension) {
+        return getExtensions().contains(extension);
+    }
+
+    @Override
+    public Collection<? extends String> getExtensions() {
+        return Arrays.asList(Info.clGetDeviceInfoStringASCII(device.address(), CL10.CL_DEVICE_EXTENSIONS).split(" "));
+    }
+
+    @Override
+    public int getComputeUnits() {
+        return Info.clGetDeviceInfoInt(device.address(), CL10.CL_DEVICE_MAX_COMPUTE_UNITS);
+    }
+
+    @Override
+    public int getClockFrequency() {
+        return Info.clGetDeviceInfoInt(device.address(), CL10.CL_DEVICE_MAX_CLOCK_FREQUENCY);
+    }
+
+    @Override
+    public int getAddressBits() {
+        return Info.clGetDeviceInfoInt(device.address(), CL10.CL_DEVICE_ADDRESS_BITS);
+    }
+
+    @Override
+    public boolean isLittleEndian() {
+        return Info.clGetDeviceInfoBoolean(device.address(), CL10.CL_DEVICE_ENDIAN_LITTLE);
+    }
+
+    @Override
+    public long getMaximumWorkItemDimensions() {
+        return Info.clGetDeviceInfoInt(device.address(), CL10.CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS);
+    }
+
+    @Override
+    public long[] getMaximumWorkItemSizes() {
+        int dim = (int) getMaximumWorkItemDimensions();
+        PointerBuffer sizes = PointerBuffer.allocateDirect(dim);
+        Info.clGetDeviceInfoPointers(device.address(), CL10.CL_DEVICE_MAX_WORK_ITEM_SIZES, sizes);
+        long[] sx = new long[dim];
+        sizes.get(sx);
+        return sx;
+    }
+
+    @Override
+    public long getMaxiumWorkItemsPerGroup() {
+        return Info.clGetDeviceInfoPointer(device.address(), CL10.CL_DEVICE_MAX_WORK_GROUP_SIZE);
+    }
+
+    @Override
+    public int getMaximumSamplers() {
+        return Info.clGetDeviceInfoInt(device.address(), CL10.CL_DEVICE_MAX_SAMPLERS);
+    }
+
+    @Override
+    public int getMaximumReadImages() {
+        return Info.clGetDeviceInfoInt(device.address(), CL10.CL_DEVICE_MAX_READ_IMAGE_ARGS);
+    }
+
+    @Override
+    public int getMaximumWriteImages() {
+        return Info.clGetDeviceInfoInt(device.address(), CL10.CL_DEVICE_MAX_WRITE_IMAGE_ARGS);
+    }
+
+    @Override
+    public long[] getMaximumImage2DSize() {
+        return new long[] {
+            Info.clGetDeviceInfoPointer(device.address(), CL10.CL_DEVICE_IMAGE2D_MAX_WIDTH),
+            Info.clGetDeviceInfoPointer(device.address(), CL10.CL_DEVICE_IMAGE2D_MAX_HEIGHT)
+        };
+    }
+
+    @Override
+    public long[] getMaximumImage3DSize() {
+        return new long[] {
+            Info.clGetDeviceInfoPointer(device.address(), CL10.CL_DEVICE_IMAGE3D_MAX_WIDTH),
+            Info.clGetDeviceInfoPointer(device.address(), CL10.CL_DEVICE_IMAGE3D_MAX_HEIGHT),
+            Info.clGetDeviceInfoPointer(device.address(), CL10.CL_DEVICE_IMAGE3D_MAX_DEPTH)
+        };
+    }
+    
+    @Override
+    public long getMaximumAllocationSize() {
+        return Info.clGetDeviceInfoLong(device.address(), CL10.CL_DEVICE_MAX_MEM_ALLOC_SIZE);
+    }
+    
+    @Override
+    public long getGlobalMemorySize() {
+        return Info.clGetDeviceInfoLong(device.address(), CL10.CL_DEVICE_GLOBAL_MEM_SIZE);
+    }
+    
+    @Override
+    public long getLocalMemorySize() {
+        return Info.clGetDeviceInfoLong(device.address(), CL10.CL_DEVICE_LOCAL_MEM_SIZE);
+    }
+    
+    @Override
+    public long getMaximumConstantBufferSize() {
+        return Info.clGetDeviceInfoLong(device.address(), CL10.CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE);
+    }
+    
+    @Override
+    public int getMaximumConstantArguments() {
+        return Info.clGetDeviceInfoInt(device.address(), CL10.CL_DEVICE_MAX_CONSTANT_ARGS);
+    }
+
+    @Override
+    public String getProfile() {
+        return Info.clGetDeviceInfoStringASCII(device.address(), CL10.CL_DEVICE_PROFILE);
+    }
+
+    @Override
+    public String getVersion() {
+        return  Info.clGetDeviceInfoStringASCII(device.address(), CL10.CL_DEVICE_VERSION);
+    }
+
+    @Override
+    public int getVersionMajor() {
+        return Utils.getMajorVersion(getVersion(), "OpenCL ");
+    }
+
+    @Override
+    public int getVersionMinor() {
+        return Utils.getMinorVersion(getVersion(), "OpenCL ");
+    }
+
+    @Override
+    public String getCompilerVersion() {
+        return  Info.clGetDeviceInfoStringASCII(device.address(), CL11.CL_DEVICE_OPENCL_C_VERSION);
+    }
+
+    @Override
+    public int getCompilerVersionMajor() {
+        return Utils.getMajorVersion(getCompilerVersion(), "OpenCL C ");
+    }
+
+    @Override
+    public int getCompilerVersionMinor() {
+        return Utils.getMinorVersion(getCompilerVersion(), "OpenCL C ");
+    }
+
+    @Override
+    public String getDriverVersion() {
+        return  Info.clGetDeviceInfoStringASCII(device.address(), CL10.CL_DRIVER_VERSION);
+    }
+
+    @Override
+    public int getDriverVersionMajor() {
+        return Utils.getMajorVersion(getDriverVersion(), "");
+    }
+
+    @Override
+    public int getDriverVersionMinor() {
+        return Utils.getMinorVersion(getDriverVersion(), "");
+    }
+
+    @Override
+    public String getName() {
+        return  Info.clGetDeviceInfoStringASCII(device.address(), CL10.CL_DEVICE_NAME);
+    }
+
+    @Override
+    public String getVendor() {
+        return  Info.clGetDeviceInfoStringASCII(device.address(), CL10.CL_DEVICE_VENDOR);
+    }
+
+    @Override
+    public String toString() {
+        return getName();
+    }
+    
+}

+ 94 - 0
jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglEvent.java

@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.lwjgl;
+
+import com.jme3.opencl.Event;
+import java.util.logging.Logger;
+import org.lwjgl.opencl.CL10;
+import org.lwjgl.opencl.Info;
+
+/**
+ *
+ * @author shaman
+ */
+public class LwjglEvent extends Event {
+    private static final Logger LOG = Logger.getLogger(LwjglEvent.class.getName());
+    private long event;
+
+    public LwjglEvent(long event) {
+        super(new ReleaserImpl(event));
+        this.event = event;
+    }
+
+    public long getEvent() {
+        return event;
+    }
+
+    @Override
+    public void waitForFinished() {
+        CL10.clWaitForEvents(event);
+        release(); //short cut to save resources
+    }
+
+    @Override
+    public boolean isCompleted() {
+        int status = Info.clGetEventInfoInt(event, CL10.CL_EVENT_COMMAND_EXECUTION_STATUS);
+        if (status == CL10.CL_SUCCESS) {
+            release(); //short cut to save resources
+            return true;
+        } else if (status < 0) {
+            Utils.checkError(status, "EventStatus");
+            return false;
+        } else {
+            return false;
+        }
+    }
+
+    private static class ReleaserImpl implements ObjectReleaser {
+        private long event;
+
+        private ReleaserImpl(long event) {
+            this.event = event;
+        }
+        
+        @Override
+        public void release() {
+            if (event != 0) {
+                int ret = CL10.clReleaseEvent(event);
+                event = 0;
+                Utils.reportError(ret, "clReleaseEvent");
+                LOG.finer("Event deleted");
+            }
+        }
+        
+    }
+}

+ 579 - 0
jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglImage.java

@@ -0,0 +1,579 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.lwjgl;
+
+import com.jme3.math.ColorRGBA;
+import com.jme3.opencl.*;
+import java.nio.ByteBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.lwjgl.opencl.*;
+
+/**
+ *
+ * @author shaman
+ */
+public class LwjglImage extends Image {
+    private static final Logger LOG = Logger.getLogger(LwjglImage.class.getName());
+
+    private final long image;
+
+    public LwjglImage(long image) {
+        super(new ReleaserImpl(image));
+        this.image = image;
+    }
+
+    public long getImage() {
+        return image;
+    }
+
+    public static int decodeImageChannelOrder(ImageChannelOrder order) {
+        switch (order) {
+            case A:
+                return CL10.CL_A;
+            case ARGB:
+                return CL10.CL_ARGB;
+            case BGRA:
+                return CL10.CL_BGRA;
+            case INTENSITY:
+                return CL10.CL_INTENSITY;
+            case LUMINANCE:
+                return CL10.CL_LUMINANCE;
+            case R:
+                return CL10.CL_R;
+            case RA:
+                return CL10.CL_RA;
+            case RG:
+                return CL10.CL_RG;
+            case RGB:
+                return CL10.CL_RGB;
+            case RGBA:
+                return CL10.CL_RGBA;
+            case RGBx:
+                return CL11.CL_RGBx;
+            case RGx:
+                return CL11.CL_RGx;
+            case Rx:
+                return CL11.CL_Rx;
+            default:
+                throw new IllegalArgumentException("unknown image channel order: " + order);
+        }
+    }
+
+    public static ImageChannelOrder encodeImageChannelOrder(int order) {
+        switch (order) {
+            case CL10.CL_A:
+                return ImageChannelOrder.A;
+            case CL10.CL_ARGB:
+                return ImageChannelOrder.ARGB;
+            case CL10.CL_BGRA:
+                return ImageChannelOrder.BGRA;
+            case CL10.CL_INTENSITY:
+                return ImageChannelOrder.INTENSITY;
+            case CL10.CL_LUMINANCE:
+                return ImageChannelOrder.LUMINANCE;
+            case CL10.CL_R:
+                return ImageChannelOrder.R;
+            case CL10.CL_RA:
+                return ImageChannelOrder.RA;
+            case CL10.CL_RG:
+                return ImageChannelOrder.RG;
+            case CL10.CL_RGB:
+                return ImageChannelOrder.RGB;
+            case CL10.CL_RGBA:
+                return ImageChannelOrder.RGBA;
+            case CL11.CL_RGBx:
+                return ImageChannelOrder.RGBx;
+            case CL11.CL_RGx:
+                return ImageChannelOrder.RGx;
+            case CL11.CL_Rx:
+                return ImageChannelOrder.Rx;
+            default:
+                //throw new com.jme3.opencl.OpenCLException("unknown image channel order id: " + order);
+                LOG.log(Level.WARNING, "Unknown image channel order id: {0}", order);
+                return null;
+        }
+    }
+
+    public static int decodeImageChannelType(ImageChannelType type) {
+        switch (type) {
+            case FLOAT:
+                return CL10.CL_FLOAT;
+            case HALF_FLOAT:
+                return CL10.CL_HALF_FLOAT;
+            case SIGNED_INT16:
+                return CL10.CL_SIGNED_INT16;
+            case SIGNED_INT32:
+                return CL10.CL_SIGNED_INT32;
+            case SIGNED_INT8:
+                return CL10.CL_SIGNED_INT8;
+            case SNORM_INT16:
+                return CL10.CL_SNORM_INT16;
+            case SNORM_INT8:
+                return CL10.CL_SNORM_INT8;
+            case UNORM_INT8:
+                return CL10.CL_UNORM_INT8;
+            case UNORM_INT_101010:
+                return CL10.CL_UNORM_INT_101010;
+            case UNORM_INT16:
+                return CL10.CL_UNORM_INT16;
+            case UNORM_SHORT_565:
+                return CL10.CL_UNORM_SHORT_565;
+            case UNORM_SHORT_555:
+                return CL10.CL_UNORM_SHORT_555;
+            case UNSIGNED_INT16:
+                return CL10.CL_UNSIGNED_INT16;
+            case UNSIGNED_INT32:
+                return CL10.CL_UNSIGNED_INT32;
+            case UNSIGNED_INT8:
+                return CL10.CL_UNSIGNED_INT8;
+            default:
+                throw new IllegalArgumentException("Unknown image channel type: " + type);
+        }
+    }
+
+    public static ImageChannelType encodeImageChannelType(int type) {
+        switch (type) {
+            case CL10.CL_FLOAT:
+                return ImageChannelType.FLOAT;
+            case CL10.CL_HALF_FLOAT:
+                return ImageChannelType.HALF_FLOAT;
+            case CL10.CL_SIGNED_INT16:
+                return ImageChannelType.SIGNED_INT16;
+            case CL10.CL_SIGNED_INT32:
+                return ImageChannelType.SIGNED_INT32;
+            case CL10.CL_SIGNED_INT8:
+                return ImageChannelType.SIGNED_INT8;
+            case CL10.CL_SNORM_INT16:
+                return ImageChannelType.SNORM_INT16;
+            case CL10.CL_SNORM_INT8:
+                return ImageChannelType.SNORM_INT8;
+            case CL10.CL_UNORM_INT8:
+                return ImageChannelType.UNORM_INT8;
+            case CL10.CL_UNORM_INT16:
+                return ImageChannelType.UNORM_INT16;
+            case CL10.CL_UNORM_INT_101010:
+                return ImageChannelType.UNORM_INT_101010;
+            case CL10.CL_UNORM_SHORT_555:
+                return ImageChannelType.UNORM_SHORT_555;
+            case CL10.CL_UNORM_SHORT_565:
+                return ImageChannelType.UNORM_SHORT_565;
+            case CL10.CL_UNSIGNED_INT16:
+                return ImageChannelType.UNSIGNED_INT16;
+            case CL10.CL_UNSIGNED_INT32:
+                return ImageChannelType.UNSIGNED_INT32;
+            case CL10.CL_UNSIGNED_INT8:
+                return ImageChannelType.UNSIGNED_INT8;
+            default:
+                //throw new com.jme3.opencl.OpenCLException("unknown image channel type id: " + type);
+                LOG.log(Level.WARNING, "Unknown image channel type id: {0}", type);
+                return null;
+        }
+    }
+
+    public static int decodeImageType(ImageType type) {
+        switch (type) {
+            case IMAGE_1D:
+                return CL12.CL_MEM_OBJECT_IMAGE1D;
+            case IMAGE_1D_ARRAY:
+                return CL12.CL_MEM_OBJECT_IMAGE1D_ARRAY;
+            case IMAGE_1D_BUFFER:
+                return CL12.CL_MEM_OBJECT_IMAGE1D_BUFFER;
+            case IMAGE_2D:
+                return CL10.CL_MEM_OBJECT_IMAGE2D;
+            case IMAGE_2D_ARRAY:
+                return CL12.CL_MEM_OBJECT_IMAGE2D_ARRAY;
+            case IMAGE_3D:
+                return CL10.CL_MEM_OBJECT_IMAGE3D;
+            default:
+                throw new IllegalArgumentException("Unknown image type: " + type);
+        }
+    }
+
+    public static ImageType encodeImageType(int type) {
+        switch (type) {
+            case CL12.CL_MEM_OBJECT_IMAGE1D:
+                return ImageType.IMAGE_1D;
+            case CL12.CL_MEM_OBJECT_IMAGE1D_ARRAY:
+                return ImageType.IMAGE_1D_ARRAY;
+            case CL12.CL_MEM_OBJECT_IMAGE1D_BUFFER:
+                return ImageType.IMAGE_1D_BUFFER;
+            case CL10.CL_MEM_OBJECT_IMAGE2D:
+                return ImageType.IMAGE_2D;
+            case CL12.CL_MEM_OBJECT_IMAGE2D_ARRAY:
+                return ImageType.IMAGE_2D_ARRAY;
+            case CL10.CL_MEM_OBJECT_IMAGE3D:
+                return ImageType.IMAGE_3D;
+            default:
+                throw new com.jme3.opencl.OpenCLException("Unknown image type id: " + type);
+        }
+    }
+
+    @Override
+    public long getWidth() {
+        return Info.clGetImageInfoPointer(image, CL10.CL_IMAGE_WIDTH);
+    }
+
+    @Override
+    public long getHeight() {
+        return Info.clGetImageInfoPointer(image, CL10.CL_IMAGE_HEIGHT);
+    }
+
+    @Override
+    public long getDepth() {
+        return Info.clGetImageInfoPointer(image, CL10.CL_IMAGE_DEPTH);
+    }
+
+    @Override
+    public long getRowPitch() {
+        return Info.clGetImageInfoPointer(image, CL10.CL_IMAGE_ROW_PITCH);
+    }
+
+    @Override
+    public long getSlicePitch() {
+        return Info.clGetImageInfoPointer(image, CL10.CL_IMAGE_SLICE_PITCH);
+    }
+
+    @Override
+    public long getArraySize() {
+        return Info.clGetImageInfoPointer(image, CL12.CL_IMAGE_ARRAY_SIZE);
+    }
+
+    @Override
+    public ImageFormat getImageFormat() {
+        Utils.b80.rewind();
+        CLImageFormat format = new CLImageFormat(Utils.b80);
+        int ret = CL10.clGetImageInfo(image, CL10.CL_IMAGE_FORMAT, format.sizeof(), Utils.b80, null);
+        Utils.checkError(ret, "clGetImageInfo");
+        return new ImageFormat(encodeImageChannelOrder(format.image_channel_order()), encodeImageChannelType(format.image_channel_data_type()));
+    }
+
+    @Override
+    public ImageType getImageType() {
+        int type = Info.clGetMemObjectInfoInt(image, CL10.CL_MEM_TYPE);
+        return encodeImageType(type);
+    }
+
+    @Override
+    public int getElementSize() {
+        return Info.clGetImageInfoInt(image, CL10.CL_IMAGE_ELEMENT_SIZE);
+    }
+
+    @Override
+    public void readImage(CommandQueue queue, ByteBuffer dest, long[] origin, long[] region, long rowPitch, long slicePitch) {
+        if (origin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[2].rewind();
+        Utils.pointerBuffers[1].put(origin).position(0);
+        Utils.pointerBuffers[2].put(region).position(0);
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10.clEnqueueReadImage(q, image, CL10.CL_TRUE, 
+                Utils.pointerBuffers[1], Utils.pointerBuffers[2], 
+                rowPitch, slicePitch, dest, null, null);
+        Utils.checkError(ret, "clEnqueueReadImage");
+    }
+
+    @Override
+    public Event readImageAsync(CommandQueue queue, ByteBuffer dest, long[] origin, long[] region, long rowPitch, long slicePitch) {
+        if (origin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointerBuffers[0].rewind();
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[2].rewind();
+        Utils.pointerBuffers[1].put(origin).position(0);
+        Utils.pointerBuffers[2].put(region).position(0);
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10.clEnqueueReadImage(q, image, CL10.CL_FALSE, 
+                Utils.pointerBuffers[1], Utils.pointerBuffers[2], 
+                rowPitch, slicePitch, dest, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueReadImage");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(event);
+    }
+
+    @Override
+    public void writeImage(CommandQueue queue, ByteBuffer dest, long[] origin, long[] region, long rowPitch, long slicePitch) {
+        if (origin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[2].rewind();
+        Utils.pointerBuffers[1].put(origin).position(0);
+        Utils.pointerBuffers[2].put(region).position(0);
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10.clEnqueueWriteImage(q, image, CL10.CL_TRUE, 
+                Utils.pointerBuffers[1], Utils.pointerBuffers[2], 
+                rowPitch, slicePitch, dest, null, null);
+        Utils.checkError(ret, "clEnqueueWriteImage");
+    }
+
+    @Override
+    public Event writeImageAsync(CommandQueue queue, ByteBuffer dest, long[] origin, long[] region, long rowPitch, long slicePitch) {
+        if (origin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointerBuffers[0].rewind();
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[2].rewind();
+        Utils.pointerBuffers[1].put(origin).position(0);
+        Utils.pointerBuffers[2].put(region).position(0);
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10.clEnqueueWriteImage(q, image, CL10.CL_FALSE, 
+                Utils.pointerBuffers[1], Utils.pointerBuffers[2], 
+                rowPitch, slicePitch, dest, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueWriteImage");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(event);
+    }
+
+    @Override
+    public void copyTo(CommandQueue queue, Image dest, long[] srcOrigin, long[] destOrigin, long[] region) {
+        if (srcOrigin.length!=3 || destOrigin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointerBuffers[0].rewind();
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[2].rewind();
+        Utils.pointerBuffers[3].rewind();
+        Utils.pointerBuffers[1].put(srcOrigin).position(0);
+        Utils.pointerBuffers[2].put(destOrigin).position(0);
+        Utils.pointerBuffers[3].put(region).position(0);
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10.clEnqueueCopyImage(q, image, ((LwjglImage) dest).getImage(), 
+                Utils.pointerBuffers[1], Utils.pointerBuffers[2], Utils.pointerBuffers[3], 
+                null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueCopyImage");
+        long event = Utils.pointerBuffers[0].get(0);
+        ret = CL10.clWaitForEvents(event);
+        Utils.checkError(ret, "clWaitForEvents");
+    }
+
+    @Override
+    public Event copyToAsync(CommandQueue queue, Image dest, long[] srcOrigin, long[] destOrigin, long[] region) {
+        if (srcOrigin.length!=3 || destOrigin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointerBuffers[0].rewind();
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[2].rewind();
+        Utils.pointerBuffers[3].rewind();
+        Utils.pointerBuffers[1].put(srcOrigin).position(0);
+        Utils.pointerBuffers[2].put(destOrigin).position(0);
+        Utils.pointerBuffers[3].put(region).position(0);
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10.clEnqueueCopyImage(q, image, ((LwjglImage) dest).getImage(), 
+                Utils.pointerBuffers[1], Utils.pointerBuffers[2], Utils.pointerBuffers[3], 
+                null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueCopyImage");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(event);
+    }
+
+    @Override
+    public ImageMapping map(CommandQueue queue, long[] origin, long[] region, MappingAccess access) {
+        if (origin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[2].rewind();
+        Utils.pointerBuffers[3].rewind();
+        Utils.pointerBuffers[4].rewind();
+        Utils.pointerBuffers[1].put(origin).position(0);
+        Utils.pointerBuffers[2].put(region).position(0);
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        long flags = Utils.getMappingAccessFlags(access);
+        Utils.errorBuffer.rewind();
+        ByteBuffer buf = CL10.clEnqueueMapImage(q, image, CL10.CL_TRUE, flags, 
+                Utils.pointerBuffers[1], Utils.pointerBuffers[2], 
+                Utils.pointerBuffers[3], Utils.pointerBuffers[4], null, null, 
+                Utils.errorBuffer, null);
+        Utils.checkError(Utils.errorBuffer, "clEnqueueMapBuffer");
+        return new ImageMapping(buf, Utils.pointerBuffers[3].get(0), Utils.pointerBuffers[4].get(0));
+    }
+
+    @Override
+    public ImageMapping mapAsync(CommandQueue queue, long[] origin, long[] region, MappingAccess access) {
+        if (origin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointerBuffers[0].rewind();
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[2].rewind();
+        Utils.pointerBuffers[3].rewind();
+        Utils.pointerBuffers[4].rewind();
+        Utils.pointerBuffers[1].put(origin).position(0);
+        Utils.pointerBuffers[2].put(region).position(0);
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        long flags = Utils.getMappingAccessFlags(access);
+        Utils.errorBuffer.rewind();
+        ByteBuffer buf = CL10.clEnqueueMapImage(q, image, CL10.CL_FALSE, flags, 
+                Utils.pointerBuffers[1], Utils.pointerBuffers[2], 
+                Utils.pointerBuffers[3], Utils.pointerBuffers[4], null, Utils.pointerBuffers[0], 
+                Utils.errorBuffer, null);
+        Utils.checkError(Utils.errorBuffer, "clEnqueueMapBuffer");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new ImageMapping(buf, Utils.pointerBuffers[3].get(0), Utils.pointerBuffers[4].get(0), new LwjglEvent(event));
+    }
+
+    @Override
+    public void unmap(CommandQueue queue, ImageMapping mapping) {
+        mapping.buffer.position(0);
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        Utils.pointerBuffers[0].rewind();
+        int ret = CL10.clEnqueueUnmapMemObject(q, image, mapping.buffer, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueUnmapMemObject");
+        long event = Utils.pointerBuffers[0].get(0);
+        ret = CL10.clWaitForEvents(event);
+        Utils.checkError(ret, "clWaitForEvents");
+    }
+
+    @Override
+    public Event fillAsync(CommandQueue queue, long[] origin, long[] region, ColorRGBA color) {
+        if (origin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointerBuffers[0].rewind();
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[2].rewind();
+        Utils.pointerBuffers[1].put(origin).position(0);
+        Utils.pointerBuffers[2].put(region).position(0);
+        Utils.tempBuffers[0].b16f.rewind();
+        Utils.tempBuffers[0].b16f.limit(4);
+        Utils.tempBuffers[0].b16f.put(color.r).put(color.g).put(color.b).put(color.a);
+        Utils.tempBuffers[0].b16.rewind();
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL12.clEnqueueFillImage(q, image, Utils.tempBuffers[0].b16, 
+                Utils.pointerBuffers[1], Utils.pointerBuffers[2], null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueFillImage");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(event);
+        //TODO: why does q.getCLEvent(event) return null?
+        //This is a bug in LWJGL: they forgot to include the line
+        //  if ( __result == CL_SUCCESS ) command_queue.registerCLEvent(event);
+        // after the native call
+    }
+
+    @Override
+    public Event fillAsync(CommandQueue queue, long[] origin, long[] region, int[] color) {
+        if (color.length != 4) {
+            throw new IllegalArgumentException("the passed color array must have length 4");
+        }
+        if (origin.length!=3 || region.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointerBuffers[0].rewind();
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[2].rewind();
+        Utils.pointerBuffers[1].put(origin).position(0);
+        Utils.pointerBuffers[2].put(region).position(0);
+        Utils.tempBuffers[0].b16i.rewind();
+        Utils.tempBuffers[0].b16i.limit(4);
+        Utils.tempBuffers[0].b16i.put(color);
+        Utils.tempBuffers[0].b16.rewind();
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL12.clEnqueueFillImage(q, image, Utils.tempBuffers[0].b16, 
+                Utils.pointerBuffers[1], Utils.pointerBuffers[2], null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueFillImage");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(event);
+    }
+
+    @Override
+    public Event copyToBufferAsync(CommandQueue queue, Buffer dest, long[] srcOrigin, long[] srcRegion, long destOffset) {
+        if (srcOrigin.length!=3 || srcRegion.length!=3) {
+            throw new IllegalArgumentException("origin and region must both be arrays of length 3");
+        }
+        Utils.pointerBuffers[0].rewind();
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[2].rewind();
+        Utils.pointerBuffers[1].put(srcOrigin).position(0);
+        Utils.pointerBuffers[2].put(srcRegion).position(0);
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10.clEnqueueCopyImageToBuffer(q, image, ((LwjglBuffer) dest).getBuffer(), 
+                Utils.pointerBuffers[1], Utils.pointerBuffers[2], destOffset, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueCopyImageToBuffer");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(event);
+    }
+
+    @Override
+    public Event acquireImageForSharingAsync(CommandQueue queue) {
+        Utils.pointerBuffers[0].rewind();
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10GL.clEnqueueAcquireGLObjects(q, image, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueAcquireGLObjects");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(event);
+    }
+    @Override
+    public void acquireImageForSharingNoEvent(CommandQueue queue) {
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10GL.clEnqueueAcquireGLObjects(q, image, null, null);
+        Utils.checkError(ret, "clEnqueueAcquireGLObjects");
+    }
+    @Override
+    public Event releaseImageForSharingAsync(CommandQueue queue) {
+        Utils.assertSharingPossible();
+        Utils.pointerBuffers[0].rewind();
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10GL.clEnqueueReleaseGLObjects(q, image, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueReleaseGLObjects");
+        long event = Utils.pointerBuffers[0].get(0);
+        return new LwjglEvent(event);
+    }
+    @Override
+    public void releaseImageForSharingNoEvent(CommandQueue queue) {
+        Utils.assertSharingPossible();
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10GL.clEnqueueReleaseGLObjects(q, image, null, null);
+        Utils.checkError(ret, "clEnqueueReleaseGLObjects");
+    }
+    
+    private static class ReleaserImpl implements ObjectReleaser {
+        private long mem;
+        private ReleaserImpl(long mem) {
+            this.mem = mem;
+        }
+        @Override
+        public void release() {
+            if (mem != 0) {
+                int ret = CL10.clReleaseMemObject(mem);
+                mem = 0;
+                Utils.reportError(ret, "clReleaseMemObject");
+            }
+        }
+        
+    }
+}

+ 233 - 0
jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglKernel.java

@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.lwjgl;
+
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector4f;
+import com.jme3.opencl.*;
+import com.jme3.opencl.Buffer;
+import java.nio.*;
+import org.lwjgl.PointerBuffer;
+import org.lwjgl.opencl.CL10;
+import org.lwjgl.opencl.CLDevice;
+import org.lwjgl.opencl.Info;
+
+/**
+ *
+ * @author shaman
+ */
+public class LwjglKernel extends Kernel {
+
+    private final long kernel;
+
+    public LwjglKernel(long kernel) {
+        super(new ReleaserImpl(kernel));
+        this.kernel = kernel;
+    }
+
+    public long getKernel() {
+        return kernel;
+    }
+    
+    @Override
+    public String getName() {
+        return Info.clGetKernelInfoStringASCII(kernel, CL10.CL_KERNEL_FUNCTION_NAME);
+    }
+
+    @Override
+    public int getArgCount() {
+        return Info.clGetKernelInfoInt(kernel, CL10.CL_KERNEL_NUM_ARGS);
+    }
+
+    @Override
+    public long getMaxWorkGroupSize(Device device) {
+        long d = ((LwjglDevice) device).getDevice();
+        return Info.clGetKernelWorkGroupInfoPointer(kernel, d, CL10.CL_KERNEL_WORK_GROUP_SIZE);
+    }
+    
+    @Override
+    public void setArg(int index, LocalMemPerElement t) {
+        int ret = CL10.clSetKernelArg (kernel, index, t.getSize() * workGroupSize.getSizes()[0] * workGroupSize.getSizes()[1] * workGroupSize.getSizes()[2]);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, LocalMem t) {
+        int ret = CL10.clSetKernelArg (kernel, index, t.getSize());
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, Buffer t) {
+        int ret = CL10.clSetKernelArg1p(kernel, index, ((LwjglBuffer) t).getBuffer());
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+    
+    @Override
+    public void setArg(int index, Image i) {
+        int ret = CL10.clSetKernelArg1p(kernel, index, ((LwjglImage) i).getImage());
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, byte b) {
+        int ret = CL10.clSetKernelArg1b(kernel, index, b);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, short s) {
+        int ret = CL10.clSetKernelArg1s(kernel, index, s);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, int i) {
+        int ret = CL10.clSetKernelArg1i(kernel, index, i);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, long l) {
+        int ret = CL10.clSetKernelArg1l(kernel, index, l);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, float f) {
+        int ret = CL10.clSetKernelArg1f(kernel, index, f);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, double d) {
+        int ret = CL10.clSetKernelArg1d(kernel, index, d);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, Vector2f v) {
+        int ret = CL10.clSetKernelArg2f(kernel, index, v.x, v.y);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, Vector4f v) {
+        int ret = CL10.clSetKernelArg4f(kernel, index, v.x, v.y, v.z, v.w);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, Quaternion q) {
+        int ret = CL10.clSetKernelArg4f(kernel, index, q.getX(), q.getY(), q.getZ(), q.getW());
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+    
+    @Override
+    public void setArg(int index, Matrix4f m) {
+        FloatBuffer buf = Utils.b80f;
+        buf.position(0);
+        buf.limit(16);
+        buf.put(m.m00).put(m.m01).put(m.m02).put(m.m03);
+        buf.put(m.m10).put(m.m11).put(m.m12).put(m.m13);
+        buf.put(m.m20).put(m.m21).put(m.m22).put(m.m23);
+        buf.put(m.m30).put(m.m31).put(m.m32).put(m.m33);
+        buf.position(0);
+        int ret = CL10.clSetKernelArg(kernel, index, buf);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public void setArg(int index, ByteBuffer buffer, long size) {
+        buffer.limit((int) (buffer.position() + size));
+        int ret = CL10.clSetKernelArg(kernel, index, buffer);
+        Utils.checkError(ret, "clSetKernelArg");
+    }
+
+    @Override
+    public Event Run(CommandQueue queue) {
+        Utils.pointerBuffers[0].rewind();
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[1].put(globalWorkSize.getSizes());
+        Utils.pointerBuffers[1].position(0);
+        PointerBuffer p2 = null;
+        if (workGroupSize.getSizes()[0] > 0) {
+            p2 = Utils.pointerBuffers[2].rewind();
+            p2.put(workGroupSize.getSizes());
+            p2.position(0);
+        }
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10.clEnqueueNDRangeKernel(q, kernel,
+			globalWorkSize.getDimension(), null, Utils.pointerBuffers[1],
+			p2, null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clEnqueueNDRangeKernel");
+        return new LwjglEvent(Utils.pointerBuffers[0].get(0));
+    }
+    @Override
+    public void RunNoEvent(CommandQueue queue) {
+        Utils.pointerBuffers[1].rewind();
+        Utils.pointerBuffers[1].put(globalWorkSize.getSizes());
+        Utils.pointerBuffers[1].position(0);
+        PointerBuffer p2 = null;
+        if (workGroupSize.getSizes()[0] > 0) {
+            p2 = Utils.pointerBuffers[2].rewind();
+            p2.put(workGroupSize.getSizes());
+            p2.position(0);
+        }
+        long q = ((LwjglCommandQueue) queue).getQueue();
+        int ret = CL10.clEnqueueNDRangeKernel(q, kernel,
+			globalWorkSize.getDimension(), null, Utils.pointerBuffers[1],
+			p2, null, null);
+        Utils.checkError(ret, "clEnqueueNDRangeKernel");
+    }
+
+    @Override
+    public ObjectReleaser getReleaser() {
+        return new ReleaserImpl(kernel);
+    }
+    private static class ReleaserImpl implements ObjectReleaser {
+        private long kernel;
+        private ReleaserImpl(long kernel) {
+            this.kernel = kernel;
+        }
+        @Override
+        public void release() {
+            if (kernel != 0) {
+                int ret = CL10.clReleaseKernel(kernel);
+                kernel = 0;
+                Utils.reportError(ret, "clReleaseKernel");
+            }
+        }
+    }
+}

+ 128 - 0
jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglPlatform.java

@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.lwjgl;
+
+import com.jme3.opencl.Device;
+import com.jme3.opencl.Platform;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import org.lwjgl.opencl.CL10;
+import org.lwjgl.opencl.CLDevice;
+import org.lwjgl.opencl.CLPlatform;
+import org.lwjgl.opencl.Info;
+
+/**
+ *
+ * @author shaman
+ */
+public final class LwjglPlatform implements Platform {
+    
+    final CLPlatform platform;
+    List<LwjglDevice> devices;
+    
+    public LwjglPlatform(CLPlatform platform) {
+        this.platform = platform;
+    }
+
+    public CLPlatform getPlatform() {
+        return platform;
+    }
+    
+    @Override
+    public List<LwjglDevice> getDevices() {
+        if (devices == null) {
+            devices = new ArrayList<>();
+            for (CLDevice d : platform.getDevices(CL10.CL_DEVICE_TYPE_ALL)) {
+                devices.add(new LwjglDevice(d, this));
+            }
+        }
+        return devices;
+    }
+
+    @Override
+    public String getProfile() {
+        return Info.clGetPlatformInfoStringASCII(platform.address(), CL10.CL_PLATFORM_PROFILE);
+    }
+
+    @Override
+    public boolean isFullProfile() {
+        return getProfile().contains("FULL_PROFILE");
+    }
+
+    @Override
+    public boolean isEmbeddedProfile() {
+        return getProfile().contains("EMBEDDED_PROFILE");
+    }
+
+    @Override
+    public String getVersion() {
+        return Info.clGetPlatformInfoStringASCII(platform.address(), CL10.CL_PLATFORM_VERSION);
+    }
+
+    @Override
+    public int getVersionMajor() {
+        return Utils.getMajorVersion(getVersion(), "OpenCL ");
+    }
+
+    @Override
+    public int getVersionMinor() {
+        return Utils.getMinorVersion(getVersion(), "OpenCL ");
+    }
+
+    @Override
+    public String getName() {
+        return Info.clGetPlatformInfoStringASCII(platform.address(), CL10.CL_PLATFORM_NAME);
+    }
+
+    @Override
+    public String getVendor() {
+        return Info.clGetPlatformInfoStringASCII(platform.address(), CL10.CL_PLATFORM_VENDOR);
+    }
+
+    @Override
+    public boolean hasExtension(String extension) {
+        return getExtensions().contains(extension);
+    }
+
+    @Override
+    public boolean hasOpenGLInterop() {
+        return hasExtension("cl_khr_gl_sharing");
+    }
+
+    @Override
+    public Collection<? extends String> getExtensions() {
+        return Arrays.asList(Info.clGetPlatformInfoStringASCII(platform.address(), CL10.CL_PLATFORM_EXTENSIONS).split(" "));
+    }
+
+}

+ 189 - 0
jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglProgram.java

@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.lwjgl;
+
+import com.jme3.opencl.*;
+import java.nio.ByteBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.lwjgl.BufferUtils;
+import org.lwjgl.PointerBuffer;
+import org.lwjgl.opencl.*;
+import org.lwjgl.system.MemoryUtil;
+import org.lwjgl.system.Pointer;
+
+/**
+ *
+ * @author shaman
+ */
+public class LwjglProgram extends Program {
+    private static final Logger LOG = Logger.getLogger(LwjglProgram.class.getName());
+    
+    private final long program;
+    private final LwjglContext context;
+
+    public LwjglProgram(long program, LwjglContext context) {
+        super(new ReleaserImpl(program));
+        this.program = program;
+        this.context = context;
+    }
+
+    public long getProgram() {
+        return program;
+    }
+
+    @Override
+    public void build(String args, Device... devices) throws KernelCompilationException {
+        PointerBuffer deviceList = null;
+        if (devices != null) {
+            deviceList = PointerBuffer.allocateDirect(devices.length);
+            deviceList.rewind();
+            for (Device d : devices) {
+                deviceList.put(((LwjglDevice) d).getDevice());
+            }
+            deviceList.flip();
+        }
+        int ret = CL10.clBuildProgram(program, deviceList, args, null, 0);
+        if (ret != CL10.CL_SUCCESS) {
+            String log = Log();
+            LOG.log(Level.WARNING, "Unable to compile program:\n{0}", log);
+            if (ret == CL10.CL_BUILD_PROGRAM_FAILURE) {
+                throw new KernelCompilationException("Failed to build program", ret, log);
+            } else {
+                Utils.checkError(ret, "clBuildProgram");
+            }
+        } else {
+            LOG.log(Level.INFO, "Program compiled:\n{0}", Log());
+        }
+    }
+    
+    private String Log(long device) {
+        Utils.pointerBuffers[0].rewind();
+        int ret = CL10.clGetProgramBuildInfo(program, device, CL10.CL_PROGRAM_BUILD_LOG, (ByteBuffer) null, Utils.pointerBuffers[0]);
+        Utils.checkError(ret, "clGetProgramBuildInfo");
+        int count = (int) Utils.pointerBuffers[0].get(0);
+        final ByteBuffer buffer = BufferUtils.createByteBuffer(count);
+        ret = CL10.clGetProgramBuildInfo(program, device, CL10.CL_PROGRAM_BUILD_LOG, buffer, null);
+        Utils.checkError(ret, "clGetProgramBuildInfo");
+        return MemoryUtil.memDecodeASCII(buffer);
+    }
+    
+    private String Log() {
+        StringBuilder str = new StringBuilder();
+        for (LwjglDevice device : context.getDevices()) {
+            long d = device.getDevice();
+            str.append(device.getName()).append(":\n");
+            //str.append(Info.clGetProgramBuildInfoStringASCII(program, d, CL10.CL_PROGRAM_BUILD_LOG)); //This throws an IllegalArgumentException in Buffer.limit()
+            str.append(Log(d));
+            str.append('\n');
+        }
+        return str.toString();
+    }
+
+    @Override
+    public Kernel createKernel(String name) {
+        long kernel = CL10.clCreateKernel(program, name, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateKernel");
+        return new LwjglKernel(kernel);
+    }
+
+    @Override
+    public Kernel[] createAllKernels() {
+        Utils.tempBuffers[0].b16i.rewind();
+        int ret = CL10.clCreateKernelsInProgram(program, null, Utils.tempBuffers[0].b16i);
+        Utils.checkError(ret, "clCreateKernelsInProgram");
+        int count = Utils.tempBuffers[0].b16i.get(0);
+        PointerBuffer buf = PointerBuffer.allocateDirect(count);
+        ret = CL10.clCreateKernelsInProgram(program, buf, null);
+        Utils.checkError(ret, "clCreateKernelsInProgram");
+        Kernel[] kx = new Kernel[count];
+        for (int i=0; i<count; ++i) {
+            kx[i] = new LwjglKernel(buf.get());
+        }
+        return kx;
+    }
+
+    @Override
+    public ByteBuffer getBinary(Device d) {
+        //throw new UnsupportedOperationException("Not supported yet, would crash the JVM");
+        
+        LwjglDevice device = (LwjglDevice) d;
+        int numDevices = Info.clGetProgramInfoInt(program, CL10.CL_PROGRAM_NUM_DEVICES);
+        
+        PointerBuffer devices = PointerBuffer.allocateDirect(numDevices);
+        int ret = CL10.clGetProgramInfo(program, CL10.CL_PROGRAM_DEVICES, devices, null);
+        Utils.checkError(ret, "clGetProgramInfo: CL_PROGRAM_DEVICES");
+        int index = -1;
+        for (int i=0; i<numDevices; ++i) {
+            if (devices.get(i) == device.getDevice()) {
+                index = i;
+            }
+        }
+        if (index == -1) {
+             throw new com.jme3.opencl.OpenCLException("Program was not built against the specified device "+device);
+        }
+        
+        PointerBuffer sizes = PointerBuffer.allocateDirect(numDevices);
+        ret = CL10.clGetProgramInfo(program, CL10.CL_PROGRAM_BINARY_SIZES, sizes, null);
+        Utils.checkError(ret, "clGetProgramInfo: CL_PROGRAM_BINARY_SIZES");
+        int size = (int) sizes.get(index);
+        
+        PointerBuffer binaryPointers = PointerBuffer.allocateDirect(numDevices);
+        for (int i=0; i<binaryPointers.capacity(); ++i) {
+            binaryPointers.put(0L);
+        }
+        binaryPointers.rewind();
+        ByteBuffer binaries = ByteBuffer.allocateDirect(size);
+        binaryPointers.put(index, binaries);
+        
+        //Fixme: why the hell does this line throw a segfault ?!?
+        ret = CL10.clGetProgramInfo(program, CL10.CL_PROGRAM_BINARIES, binaryPointers, null);
+        Utils.checkError(ret, "clGetProgramInfo: CL_PROGRAM_BINARIES");
+        
+        return binaries;
+    }
+
+    private static class ReleaserImpl implements ObjectReleaser {
+        private long program;
+        private ReleaserImpl(long program) {
+            this.program = program;
+        }
+        @Override
+        public void release() {
+            if (program != 0) {
+                int ret = CL10.clReleaseProgram(program);
+                program = 0;
+                Utils.reportError(ret, "clReleaseProgram");
+            }
+        }
+    }
+}

+ 163 - 0
jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/Utils.java

@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2009-2016 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.opencl.lwjgl;
+
+import com.jme3.opencl.MappingAccess;
+import com.jme3.opencl.MemoryAccess;
+import com.jme3.opencl.OpenCLException;
+import java.nio.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.lwjgl.BufferUtils;
+import org.lwjgl.PointerBuffer;
+import org.lwjgl.opencl.*;
+
+
+/**
+ *
+ * @author shaman
+ */
+public class Utils {
+    private static final Logger LOG = Logger.getLogger(Utils.class.getName());
+    private Utils() {}
+   
+    public static final boolean CL_GL_SHARING_POSSIBLE = com.jme3.system.lwjgl.LwjglContext.CL_GL_SHARING_POSSIBLE;
+    public static void assertSharingPossible() {
+        if (!CL_GL_SHARING_POSSIBLE) {
+            throw new OpenCLException("OpenGL/CL sharing not possible");
+        }
+    }
+    
+    public static int getMajorVersion(String version, String prefix) {
+        String s = version.substring(prefix.length());
+        return Integer.parseInt(s);
+    }
+    
+    public static int getMinorVersion(String version, String prefix) {
+        String s = version.substring(prefix.length());
+        int major = Integer.parseInt(s);
+        s = s.substring((int) (Math.log10(major) + 2));
+        return Integer.parseInt(s);
+    }
+    
+    public static final class TempBuffer {
+        public final ByteBuffer b16;
+        public final ShortBuffer b16s;
+        public final IntBuffer b16i;
+        public final LongBuffer b16l;
+        public final FloatBuffer b16f;
+        public final DoubleBuffer b16d;
+        public TempBuffer() {
+            b16 = BufferUtils.createByteBuffer(16);
+            b16s = b16.asShortBuffer();
+            b16i = b16.asIntBuffer();
+            b16l = b16.asLongBuffer();
+            b16f = b16.asFloatBuffer();
+            b16d = b16.asDoubleBuffer();
+        }
+    }
+    public static final ByteBuffer b80; //needed for ImageDescriptor
+    public static final LongBuffer b80l;
+    public static final FloatBuffer b80f;
+    public static final TempBuffer[] tempBuffers = new TempBuffer[8];
+    public static final PointerBuffer[] pointerBuffers = new PointerBuffer[8];
+    static {
+        for (int i=0; i<8; ++i) {
+            tempBuffers[i] = new TempBuffer();
+            pointerBuffers[i] = PointerBuffer.allocateDirect(4);
+        }
+        errorBuffer = BufferUtils.createIntBuffer(1);
+        b80 = BufferUtils.createByteBuffer(80);
+        b80l = b80.asLongBuffer();
+        b80f = b80.asFloatBuffer();
+    }
+    
+    public static IntBuffer errorBuffer;
+    public static void checkError(IntBuffer errorBuffer, String callName) {
+        checkError(errorBuffer.get(0), callName);
+    }
+    public static void checkError(int error, String callName) {
+        if (error != CL10.CL_SUCCESS) {
+            String errname = getErrorName(error);
+            if (errname == null) {
+                errname = "UNKNOWN";
+            }
+            throw new OpenCLException("OpenCL error in " + callName + ": " + errname + " (0x" + Integer.toHexString(error) + ")", error);
+        }
+    }
+    
+    public static void reportError(int error, String callName) {
+        if (error != CL10.CL_SUCCESS) {
+            String errname = getErrorName(error);
+            if (errname == null) {
+                errname = "UNKNOWN";
+            }
+            LOG.log(Level.WARNING, "OpenCL error in {0}: {1} (0x{2})", new Object[]{callName, errname, Integer.toHexString(error)});
+        }
+    }
+    
+    public static String getErrorName(int code) {
+        return CLUtil.getErrcodeName(code);
+    }
+    
+    public static long getMemoryAccessFlags(MemoryAccess ma) {
+        switch (ma) {
+            case READ_ONLY: return CL10.CL_MEM_READ_ONLY;
+            case WRITE_ONLY: return CL10.CL_MEM_WRITE_ONLY;
+            case READ_WRITE: return CL10.CL_MEM_READ_WRITE;
+            default: throw new IllegalArgumentException("Unknown memory access: "+ma);
+        }
+    }
+    public static MemoryAccess getMemoryAccessFromFlag(long flag) {
+        if ((flag & CL10.CL_MEM_READ_WRITE) > 0) {
+            return MemoryAccess.READ_WRITE;
+        }
+        if ((flag & CL10.CL_MEM_READ_ONLY) > 0) {
+            return MemoryAccess.READ_ONLY;
+        }
+        if ((flag & CL10.CL_MEM_WRITE_ONLY) > 0) {
+            return MemoryAccess.WRITE_ONLY;
+        }
+        throw new OpenCLException("Unknown memory access flag: "+flag);
+    }
+    
+    public static long getMappingAccessFlags(MappingAccess ma) {
+        switch (ma) {
+            case MAP_READ_ONLY: return CL10.CL_MAP_READ;
+            case MAP_READ_WRITE: return CL10.CL_MAP_READ | CL10.CL_MAP_WRITE;
+            case MAP_WRITE_ONLY: return CL10.CL_MAP_WRITE;
+            case MAP_WRITE_INVALIDATE: return CL12.CL_MAP_WRITE_INVALIDATE_REGION;
+            default: throw new IllegalArgumentException("Unknown mapping access: "+ma);
+        }
+    }
+
+}

+ 168 - 5
jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java

@@ -35,6 +35,13 @@ package com.jme3.system.lwjgl;
 import com.jme3.input.lwjgl.GlfwJoystickInput;
 import com.jme3.input.lwjgl.GlfwKeyInput;
 import com.jme3.input.lwjgl.GlfwMouseInput;
+import com.jme3.opencl.Context;
+import com.jme3.opencl.DefaultPlatformChooser;
+import com.jme3.opencl.Device;
+import com.jme3.opencl.PlatformChooser;
+import com.jme3.opencl.lwjgl.LwjglDevice;
+import com.jme3.opencl.lwjgl.LwjglPlatform;
+import com.jme3.opencl.lwjgl.Utils;
 import com.jme3.renderer.Renderer;
 import com.jme3.renderer.RendererException;
 import com.jme3.renderer.lwjgl.LwjglGL;
@@ -43,17 +50,21 @@ import com.jme3.renderer.lwjgl.LwjglGLFboEXT;
 import com.jme3.renderer.lwjgl.LwjglGLFboGL3;
 import com.jme3.renderer.opengl.*;
 import com.jme3.system.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.lwjgl.PointerBuffer;
 import org.lwjgl.glfw.GLFW;
+import org.lwjgl.opencl.*;
+import org.lwjgl.opengl.ARBDebugOutput;
 import org.lwjgl.opengl.ARBFramebufferObject;
 import org.lwjgl.opengl.EXTFramebufferMultisample;
 import org.lwjgl.opengl.GLCapabilities;
 
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.logging.Level;
-import java.util.logging.Logger;
 import static org.lwjgl.glfw.GLFW.GLFW_TRUE;
-import org.lwjgl.opengl.ARBDebugOutput;
-
+import static org.lwjgl.opencl.CL10.CL_CONTEXT_PLATFORM;
 import static org.lwjgl.opengl.GL.createCapabilities;
 import static org.lwjgl.opengl.GL11.glGetInteger;
 
@@ -64,6 +75,8 @@ public abstract class LwjglContext implements JmeContext {
 
     private static final Logger logger = Logger.getLogger(LwjglContext.class.getName());
 
+    public static final boolean CL_GL_SHARING_POSSIBLE = true;
+    
     protected static final String THREAD_NAME = "jME3 Main";
 
     protected AtomicBoolean created = new AtomicBoolean(false);
@@ -77,6 +90,8 @@ public abstract class LwjglContext implements JmeContext {
     protected GlfwJoystickInput joyInput;
     protected Timer timer;
     protected SystemListener listener;
+    
+    protected com.jme3.opencl.lwjgl.LwjglContext clContext;
 
     public void setSystemListener(SystemListener listener) {
         this.listener = listener;
@@ -180,6 +195,149 @@ public abstract class LwjglContext implements JmeContext {
             joyInput.initialize();
         }
         renderable.set(true);
+        
+    }
+    
+    protected void initOpenCL(long window) {
+        logger.info("Initialize OpenCL with LWJGL3");
+        
+//        try {
+//            CL.create();
+//        } catch (Exception ex) {
+//            logger.log(Level.SEVERE, "Unable to initialize OpenCL", ex);
+//            return;
+//        }
+        
+        //load platforms and devices
+        StringBuilder platformInfos = new StringBuilder();
+        ArrayList<LwjglPlatform> platforms = new ArrayList<>();
+        for (CLPlatform p : CLPlatform.getPlatforms()) {
+            platforms.add(new LwjglPlatform(p));
+        }
+        platformInfos.append("Available OpenCL platforms:");
+        for (int i=0; i<platforms.size(); ++i) {
+            LwjglPlatform platform = platforms.get(i);
+            platformInfos.append("\n * Platform ").append(i+1);
+            platformInfos.append("\n *   Name: ").append(platform.getName());
+            platformInfos.append("\n *   Vendor: ").append(platform.getVendor());
+            platformInfos.append("\n *   Version: ").append(platform.getVersion());
+            platformInfos.append("\n *   Profile: ").append(platform.getProfile());
+            platformInfos.append("\n *   Supports interop: ").append(platform.hasOpenGLInterop());
+            List<LwjglDevice> devices = platform.getDevices();
+            platformInfos.append("\n *   Available devices:");
+            for (int j=0; j<devices.size(); ++j) {
+                LwjglDevice device = devices.get(j);
+                platformInfos.append("\n *    * Device ").append(j+1);
+                platformInfos.append("\n *    *   Name: ").append(device.getName());
+                platformInfos.append("\n *    *   Vendor: ").append(device.getVendor());
+                platformInfos.append("\n *    *   Version: ").append(device.getVersion());
+                platformInfos.append("\n *    *   Profile: ").append(device.getProfile());
+                platformInfos.append("\n *    *   Compiler version: ").append(device.getCompilerVersion());
+                platformInfos.append("\n *    *   Device type: ").append(device.getDeviceType());
+                platformInfos.append("\n *    *   Compute units: ").append(device.getComputeUnits());
+                platformInfos.append("\n *    *   Work group size: ").append(device.getMaxiumWorkItemsPerGroup());
+                platformInfos.append("\n *    *   Global memory: ").append(device.getGlobalMemorySize()).append("B");
+                platformInfos.append("\n *    *   Local memory: ").append(device.getLocalMemorySize()).append("B");
+                platformInfos.append("\n *    *   Constant memory: ").append(device.getMaximumConstantBufferSize()).append("B");
+                platformInfos.append("\n *    *   Supports double: ").append(device.hasDouble());
+                platformInfos.append("\n *    *   Supports half floats: ").append(device.hasHalfFloat());
+                platformInfos.append("\n *    *   Supports writable 3d images: ").append(device.hasWritableImage3D());
+                platformInfos.append("\n *    *   Supports interop: ").append(device.hasOpenGLInterop());
+            }
+        }
+        logger.info(platformInfos.toString());
+        
+        //choose devices
+        PlatformChooser chooser = null;
+        if (settings.getOpenCLPlatformChooser() != null) {
+            try {
+                chooser = (PlatformChooser) Class.forName(settings.getOpenCLPlatformChooser()).newInstance();
+            } catch (Exception ex) {
+                logger.log(Level.WARNING, "unable to instantiate custom PlatformChooser", ex);
+            }
+        }
+        if (chooser == null) {
+            chooser = new DefaultPlatformChooser();
+        }
+        List<? extends Device> choosenDevices = chooser.chooseDevices(platforms);
+        List<CLDevice> devices = new ArrayList<>(choosenDevices.size());
+        LwjglPlatform platform = null;
+        for (Device d : choosenDevices) {
+            if (!(d instanceof LwjglDevice)) {
+                logger.log(Level.SEVERE, "attempt to return a custom Device implementation from PlatformChooser: {0}", d);
+                return;
+            }
+            LwjglDevice ld = (LwjglDevice) d;
+            if (platform == null) {
+                platform = ld.getPlatform();
+            } else if (platform != ld.getPlatform()) {
+                logger.severe("attempt to use devices from different platforms");
+                return;
+            }
+            devices.add(ld.getCLDevice());
+        }
+        if (devices.isEmpty()) {
+            logger.warning("no devices specified, no OpenCL context created");
+            return;
+        }
+        logger.log(Level.INFO, "chosen platform: {0}", platform.getName());
+        logger.log(Level.INFO, "chosen devices: {0}", choosenDevices);
+        
+        //create context
+        try {
+            long c = createContext(platform.getPlatform(), devices, window);
+            clContext = new com.jme3.opencl.lwjgl.LwjglContext(c, (List<LwjglDevice>) choosenDevices);
+        } catch (Exception ex) {
+            logger.log(Level.SEVERE, "Unable to create OpenCL context", ex);
+            return;
+        }
+        
+        logger.info("OpenCL context created");
+    }
+    private long createContext(final CLPlatform platform, final List<CLDevice> devices, long window) throws Exception {
+        
+        final int propertyCount = 2 + 4 + 1;
+
+        final PointerBuffer properties = PointerBuffer.allocateDirect(propertyCount + devices.size());
+        
+        //set sharing properties
+        //https://github.com/glfw/glfw/issues/104
+        //https://github.com/LWJGL/lwjgl3/blob/master/modules/core/src/test/java/org/lwjgl/demo/opencl/Mandelbrot.java
+        //TODO: test on Linus and MacOSX
+        switch (org.lwjgl.system.Platform.get()) {
+            case WINDOWS:
+                properties
+                        .put(KHRGLSharing.CL_GL_CONTEXT_KHR)
+                        .put(org.lwjgl.glfw.GLFWNativeWGL.glfwGetWGLContext(window))
+                        .put(KHRGLSharing.CL_WGL_HDC_KHR)
+                        .put(org.lwjgl.opengl.WGL.wglGetCurrentDC());
+                break;
+            case LINUX:
+                properties
+                        .put(KHRGLSharing.CL_GL_CONTEXT_KHR)
+                        .put(org.lwjgl.glfw.GLFWNativeGLX.glfwGetGLXContext(window))
+                        .put(KHRGLSharing.CL_GLX_DISPLAY_KHR)
+                        .put(org.lwjgl.glfw.GLFWNativeX11.glfwGetX11Display());
+                break;
+            case MACOSX:
+                properties
+                        .put(APPLEGLSharing.CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE)
+                        .put(org.lwjgl.opengl.CGL.CGLGetShareGroup(org.lwjgl.opengl.CGL.CGLGetCurrentContext()));
+        }
+        properties.put(CL_CONTEXT_PLATFORM).put(platform);
+        properties.put(0);
+        properties.flip();
+
+        Utils.errorBuffer.rewind();
+        PointerBuffer deviceBuffer = PointerBuffer.allocateDirect(devices.size());
+        for (CLDevice d : devices) {
+            deviceBuffer.put(d);
+        }
+        deviceBuffer.flip();
+        long context = CL10.clCreateContext(properties, deviceBuffer, null, 0, Utils.errorBuffer);
+        Utils.checkError(Utils.errorBuffer, "clCreateContext");
+        
+        return context;
     }
 
     public void internalDestroy() {
@@ -250,4 +408,9 @@ public abstract class LwjglContext implements JmeContext {
         return timer;
     }
 
+    @Override
+    public Context getOpenCLContext() {
+        return clContext;
+    }
+
 }

+ 7 - 0
jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java

@@ -326,6 +326,13 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
 
             created.set(true);
             super.internalCreate();
+            
+            //create OpenCL
+            //Must be done here because the window handle is needed
+            if (settings.isOpenCLSupport()) {
+                initOpenCL(window);
+            }
+            
         } catch (Exception ex) {
             try {
                 if (window != NULL) {