Merge pull request #494 from shamanDevel/OpenCL

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

@ -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;
}
}

@ -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();
}
}

@ -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();
}

@ -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);
}

@ -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
}
}

@ -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();
}

@ -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();
}

@ -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
}

@ -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;
}
}
}

@ -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;
}
}

@ -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
}

@ -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
}

@ -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;
}
}

@ -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();
}

@ -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();
}
}

@ -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();
}

@ -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);
}

@ -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);
}

@ -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();
}
}
}
}

@ -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

@ -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();
}

@ -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");
}
}

@ -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.
*/

@ -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;
}
}

@ -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

@ -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

@ -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

@ -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(){
}

@ -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')

@ -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;
}
}

@ -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);
}
}
}

@ -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");
}
}
}
}

@ -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;
}
}

@ -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);
}
}

@ -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);
}
}
}

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

@ -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>

@ -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);
}

@ -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;
}
}

@ -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'
}

@ -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");
}
}
}
}

@ -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");
}
}
}
}

@ -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");
}
}
}
}

@ -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();
}
}

@ -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");
}
}
}
}

@ -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");
}
}
}
}

@ -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");
}
}
}
}

@ -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();
}
}

@ -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");
}
}
}
}

@ -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);
}
}
}

@ -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;
}
}

@ -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");
}
}
}
}

@ -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");
}
}
}
}

@ -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");
}
}
}
}

@ -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();
}
}

@ -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");
}
}
}
}

@ -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");
}
}
}
}

@ -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");
}
}
}
}

@ -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(" "));
}
}

@ -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");
}
*/
}
}
}

@ -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);
}
}
}

@ -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;
}
}

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

@ -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");
}
}
}
}

@ -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");
}
}
}
}

@ -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");
}
}
}
}

@ -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();
}
}

@ -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");
}
}
}
}

@ -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");
}
}
}
}

@ -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");
}
}
}
}

@ -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(" "));
}
}

@ -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");
}
}
}
}

@ -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);
}
}
}

@ -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;
}
}

@ -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) {

Loading…
Cancel
Save