diff --git a/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java b/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java index 991ad25c0..35c3c3911 100644 --- a/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java +++ b/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java @@ -457,4 +457,10 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex } }); } + + @Override + public com.jme3.opencl.Context getOpenCLContext() { + logger.warning("OpenCL is not yet supported on android"); + return null; + } } diff --git a/jme3-core/src/main/java/com/jme3/opencl/AbstractOpenCLObject.java b/jme3-core/src/main/java/com/jme3/opencl/AbstractOpenCLObject.java new file mode 100644 index 000000000..fce0f481d --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/opencl/AbstractOpenCLObject.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl; + +/** + * Abstract implementation of {@link OpenCLObject} providing the release + * mechanisms. + * @author Sebastian Weiss + */ +public abstract class AbstractOpenCLObject implements OpenCLObject { + + protected final ObjectReleaser releaser; + protected AbstractOpenCLObject(ObjectReleaser releaser) { + this.releaser = releaser; + } + @Override + public void register() { + OpenCLObjectManager.getInstance().registerObject(this); + } + @Override + public void release() { + releaser.release(); + } + @Override + @SuppressWarnings("FinalizeDeclaration") + protected void finalize() throws Throwable { + release(); + } + @Override + public ObjectReleaser getReleaser() { + return releaser; + } +} diff --git a/jme3-core/src/main/java/com/jme3/opencl/Buffer.java b/jme3-core/src/main/java/com/jme3/opencl/Buffer.java new file mode 100644 index 000000000..ebb7d2bb2 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/opencl/Buffer.java @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl; + +import java.nio.ByteBuffer; + +/** + * Wrapper for an OpenCL buffer object. + * A buffer object stores a one-dimensional collection of elements. Elements of a buffer object can + * be a scalar data type (such as an int, float), vector data type, or a user-defined structure. + *
+ * Buffers are created by the {@link Context}. + *
+ * 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.
+ * Important: 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. + * Important: 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() }. + * Important: 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.
+ * Important: 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. + * Important: 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() }. + * Important: 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. + *
+ * 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(); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/opencl/CommandQueue.java b/jme3-core/src/main/java/com/jme3/opencl/CommandQueue.java new file mode 100644 index 000000000..6d54237f9 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/opencl/CommandQueue.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl; + +/** + * Wrapper for an OpenCL command queue. + * The command queue serializes every GPU function call: By passing the same + * queue to OpenCL function (buffer, image operations, kernel calls), it is + * ensured that they are executed in the order in which they are passed. + *
+ * 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(); + +} diff --git a/jme3-core/src/main/java/com/jme3/opencl/Context.java b/jme3-core/src/main/java/com/jme3/opencl/Context.java new file mode 100644 index 000000000..fed3cd2a3 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/opencl/Context.java @@ -0,0 +1,439 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl; + +import com.jme3.asset.AssetInfo; +import com.jme3.asset.AssetKey; +import com.jme3.asset.AssetManager; +import com.jme3.asset.AssetNotFoundException; +import com.jme3.opencl.Image.ImageDescriptor; +import com.jme3.opencl.Image.ImageFormat; +import com.jme3.opencl.Image.ImageType; +import com.jme3.scene.VertexBuffer; +import com.jme3.texture.FrameBuffer; +import com.jme3.texture.Texture; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.StringReader; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * The central OpenCL context. Every action starts from here. + * The context can be obtained by {@link com.jme3.system.JmeContext#getOpenCLContext() }. + *

+ * The context is used to: + *

+ * @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. + *
+ * The available devices were specified by a {@link PlatformChooser}. + * @return + */ + public abstract List 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.
+ * {@code ImageFormat} specifies the element type and order, like RGBA of floats.
+ * {@code ImageDescriptor} specifies the dimension of the image.
+ * 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. + *
+ * 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). + *
+ * Note: The vertex buffer must already been uploaded to the GPU, + * i.e. it must be used at least once for drawing. + *

+ * 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). + *
+ * Note: The image must already been uploaded to the GPU, + * i.e. it must be used at least once for drawing. + *

+ * 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). + *
+ * Note: The image must already been uploaded to the GPU, + * i.e. it must be used at least once for drawing. + *

+ * 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. + *

+ * 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. + *
+ * This can be used as an alternative to post processing effects + * (e.g. reduce sum operations, needed e.g. for tone mapping). + *
+ * Note: The renderbuffer must already been uploaded to the GPU, + * i.e. it must be used at least once for drawing. + *

+ * 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(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. + *

+ * The typical use case is: + *

+ * + * 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. + *

+ * The typical use case is: + *

+ * + * 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 resources) { + StringBuilder str = new StringBuilder(); + str.append(include); + for (String res : resources) { + AssetInfo info = assetManager.locateAsset(new AssetKey(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 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...) }. + * Important: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); +} diff --git a/jme3-core/src/main/java/com/jme3/opencl/DefaultPlatformChooser.java b/jme3-core/src/main/java/com/jme3/opencl/DefaultPlatformChooser.java new file mode 100644 index 000000000..8f8a53505 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/opencl/DefaultPlatformChooser.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +/** + * A default implementation of {@link PlatformChooser}. + * It favors GPU devices with OpenGL sharing, then any devices with OpenGL sharing, + * then any possible device. + * @author shaman + */ +public class DefaultPlatformChooser implements PlatformChooser { + private static final Logger LOG = Logger.getLogger(DefaultPlatformChooser.class.getName()); + + @Override + public List chooseDevices(List platforms) { + ArrayList result = new ArrayList(); + 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 + } + +} diff --git a/jme3-core/src/main/java/com/jme3/opencl/Device.java b/jme3-core/src/main/java/com/jme3/opencl/Device.java new file mode 100644 index 000000000..d34854c0c --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/opencl/Device.java @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl; + +import java.util.Collection; + +/** + * Represents a hardware device actually running the OpenCL kernels. + * A {@link Context} can be accociated with multiple {@code Devices} + * that all belong to the same {@link Platform}. + * For execution, a single device must be chosen and passed to a command + * queue ({@link Context#createQueue(com.jme3.opencl.Device) }). + *

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

+ * Most methods take long arrays as input: {@code long[] origin} and {@code long[] region}. + * Both are arrays of length 3. + *
+ * origin 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. + *
+ * region 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. + *

+ * There exists two constructors:
+ * {@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).
+ * {@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. + * Important: 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. + * Important: 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 only 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 not 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 +} diff --git a/jme3-core/src/main/java/com/jme3/opencl/Kernel.java b/jme3-core/src/main/java/com/jme3/opencl/Kernel.java new file mode 100644 index 000000000..09f22a813 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/opencl/Kernel.java @@ -0,0 +1,628 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl; + +import com.jme3.math.*; +import com.jme3.util.TempVars; +import java.nio.ByteBuffer; +import java.util.Arrays; + +/** + * Wrapper for an OpenCL kernel, a piece of executable code on the GPU. + *

+ * Terminology:
+ * A Kernel is executed in parallel. In total number of parallel threads, + * called work items, are specified by the global work size (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}. + *
+ * Not all threads can always be executed in parallel because there simply might + * not be enough processor cores. + * Therefore, the concept of a work group 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: shared memory + * 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.
+ * 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. + * + *

+ * There are two ways to launch a kernel:
+ * 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) }.
+ * 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.
+ * The argument must be a known type: + * {@code LocalMemPerElement, LocalMem, Image, Buffer, byte, short, int, + * long, float, double, Vector2f, Vector4f, Quaternion, Matrix3f, Matrix4f}. + *
+ * 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; + } + } + +} diff --git a/jme3-core/src/main/java/com/jme3/opencl/KernelCompilationException.java b/jme3-core/src/main/java/com/jme3/opencl/KernelCompilationException.java new file mode 100644 index 000000000..a0e20c9e5 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/opencl/KernelCompilationException.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl; + +/** + * This exception is thrown by {@link Program#build() } and {@link Program#build(java.lang.String) } + * when the compilation failed. + * The error log returned by {@link #getLog() } contains detailed information + * where the error occured. + * @author shaman + */ +public class KernelCompilationException extends OpenCLException { + + private final String log; + + public KernelCompilationException(String msg, int errorCode, String log) { + super(msg, errorCode); + this.log = log; + } + + /** + * The output of the compiler + * @return + */ + public String getLog() { + return log; + } + +} \ No newline at end of file diff --git a/jme3-core/src/main/java/com/jme3/opencl/MappingAccess.java b/jme3-core/src/main/java/com/jme3/opencl/MappingAccess.java new file mode 100644 index 000000000..121c6c8cb --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/opencl/MappingAccess.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl; + +/** + * Specifies the access flags when mapping a {@link Buffer} or {@link Image} object. + * @see Buffer#map(com.jme3.opencl.CommandQueue, long, long, com.jme3.opencl.MappingAccess) + * @see Image#map(com.jme3.opencl.CommandQueue, long[], long[], com.jme3.opencl.MappingAccess) + * @author shaman + */ +public enum MappingAccess { + /** + * Only read access is allowed to the mapped memory. + */ + MAP_READ_ONLY, + /** + * Only write access is allowed to the mapped memory. + */ + MAP_WRITE_ONLY, + /** + * Both read and write access is allowed. + */ + MAP_READ_WRITE, + /** + * The old memory content is completely discarded and the buffer is filled + * completely with new data. This might be faster than {@link #MAP_WRITE_ONLY} + */ + MAP_WRITE_INVALIDATE +} diff --git a/jme3-core/src/main/java/com/jme3/opencl/MemoryAccess.java b/jme3-core/src/main/java/com/jme3/opencl/MemoryAccess.java new file mode 100644 index 000000000..d5071665e --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/opencl/MemoryAccess.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl; + +/** + * Specifies how a buffer object can be accessed by the kernel. + * @author shaman + * @see Buffer + */ +public enum MemoryAccess { + /** + * A kernel can both read and write the buffer. + */ + READ_WRITE, + /** + * A kernel can only write this buffer. + */ + WRITE_ONLY, + /** + * A kernel can only read this buffer + */ + READ_ONLY +} diff --git a/jme3-core/src/main/java/com/jme3/opencl/OpenCLException.java b/jme3-core/src/main/java/com/jme3/opencl/OpenCLException.java new file mode 100644 index 000000000..fd0836ac9 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/opencl/OpenCLException.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl; + +/** + * Generic OpenCL exception, can be thrown in every method of this package. + * The error code and its name is reported in the message string as well as the OpenCL call that + * causes this exception. Please refer to the official OpenCL specification + * to see what might cause this exception. + * @author shaman + */ +public class OpenCLException extends RuntimeException { + private static final long serialVersionUID = 8471229972153694848L; + + private final int errorCode; + + /** + * Creates a new instance of OpenCLExceptionn without detail + * message. + */ + public OpenCLException() { + errorCode = 0; + } + + /** + * Constructs an instance of OpenCLExceptionn 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; + } + + +} diff --git a/jme3-core/src/main/java/com/jme3/opencl/OpenCLObject.java b/jme3-core/src/main/java/com/jme3/opencl/OpenCLObject.java new file mode 100644 index 000000000..f9d631346 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/opencl/OpenCLObject.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl; + +/** + * Base interface of all native OpenCL objects. + * This interface provides the functionality for savely release the object. + * @author shaman + */ +public interface OpenCLObject { + + /** + * Releaser for an {@link OpenCLObject}. + * Implementations of this interface must not hold a reference to the + * {@code OpenCLObject} directly. + */ + public static interface ObjectReleaser { + /** + * Releases the native resources of the associated {@link OpenCLObject}. + * This method must be guarded against multiple calls: only the first + * call should release, the next ones must not throw an exception. + */ + void release(); + } + /** + * Returns the releaser object. Multiple calls should return the same object. + * The ObjectReleaser is used to release the OpenCLObject when it is garbage + * collected. Therefore, the returned object must not hold a reference to + * the OpenCLObject. + * @return the object releaser + */ + ObjectReleaser getReleaser(); + /** + * Releases this native object. + * Should delegate to {@code getReleaser().release()}. + */ + void release(); + /** + * Registers this object for automatic releasing on garbage collection. + * By default, OpenCLObjects are not registered in the + * {@link OpenCLObjectManager}, you have to release it manually + * by calling {@link #release() }. + * Without registering or releasing, a memory leak might occur. + */ + void register(); +} diff --git a/jme3-core/src/main/java/com/jme3/opencl/OpenCLObjectManager.java b/jme3-core/src/main/java/com/jme3/opencl/OpenCLObjectManager.java new file mode 100644 index 000000000..4b761da5b --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/opencl/OpenCLObjectManager.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl; + +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; +import java.util.HashSet; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author shaman + */ +public class OpenCLObjectManager { + private static final Logger LOG = Logger.getLogger(OpenCLObjectManager.class.getName()); + private static final Level LOG_LEVEL1 = Level.FINER; + private static final Level LOG_LEVEL2 = Level.FINE; + /** + * Call Runtime.getRuntime().gc() every these frames + */ + private static final int GC_FREQUENCY = 10; + + private static final OpenCLObjectManager INSTANCE = new OpenCLObjectManager(); + private OpenCLObjectManager() {} + + public static OpenCLObjectManager getInstance() { + return INSTANCE; + } + + private ReferenceQueue refQueue = new ReferenceQueue(); + private HashSet activeObjects = new HashSet(); + private int gcCounter = 0; + + private static class OpenCLObjectRef extends PhantomReference { + + private OpenCLObject.ObjectReleaser releaser; + + public OpenCLObjectRef(ReferenceQueue 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(); + } +} diff --git a/jme3-core/src/main/java/com/jme3/opencl/Platform.java b/jme3-core/src/main/java/com/jme3/opencl/Platform.java new file mode 100644 index 000000000..10b3fd466 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/opencl/Platform.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl; + +import java.util.Collection; +import java.util.List; + +/** + * A wrapper for an OpenCL platform. A platform is the highest object in the + * object hierarchy, it creates the {@link Device}s which are then used to + * create the {@link Context}.
+ * This class is mostly used within {@link PlatformChooser}. + * + * @author shaman + */ +public interface Platform { + + /** + * @return the list of available devices for this platform + */ + List 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 getExtensions(); + +} diff --git a/jme3-core/src/main/java/com/jme3/opencl/PlatformChooser.java b/jme3-core/src/main/java/com/jme3/opencl/PlatformChooser.java new file mode 100644 index 000000000..a1646df25 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/opencl/PlatformChooser.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl; + +import com.jme3.system.AppSettings; +import java.util.List; + +/** + * This SPI is called on startup to specify which platform and which devices + * are used for context creation. + * @author shaman + * @see AppSettings#setOpenCLPlatformChooser(java.lang.Class) + */ +public interface PlatformChooser { + + /** + * Chooses one or more devices for the opencl context. + * All returned devices must belong to the same platform. + * If the returned list is empty, no context will be created. + * @param platforms the available platforms + * @return the list of devices + */ + List chooseDevices(List platforms); + +} diff --git a/jme3-core/src/main/java/com/jme3/opencl/Program.java b/jme3-core/src/main/java/com/jme3/opencl/Program.java new file mode 100644 index 000000000..a98cf2c02 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/opencl/Program.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl; + +import java.nio.ByteBuffer; + +/** + * A wrapper for an OpenCL program. A program is created from kernel source code, + * manages the build process and creates the kernels. + *

+ * 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); + +} diff --git a/jme3-core/src/main/java/com/jme3/opencl/ProgramCache.java b/jme3-core/src/main/java/com/jme3/opencl/ProgramCache.java new file mode 100644 index 000000000..bc533acaf --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/opencl/ProgramCache.java @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl; + +import com.jme3.system.JmeSystem; +import com.jme3.util.BufferUtils; +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Implements a simple cache system for program objects. + * The program objects are saved persistently with {@link #saveToCache(java.lang.String, com.jme3.opencl.Program) }. + * On the next run, the stored programs can then be loaded + * with {@link #loadFromCache(java.lang.String, java.lang.String) }. + *
+ * The programs are identified by a unique id. The following format is recommended: + * {@code id = .}. + * + * @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.
+ * 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}. + *

+ * 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 = .}. + *

+ * 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) }. + *
+ * The id must be unique, otherwise collisions within the cache occur. + * Therefore, the following naming schema is recommended: + * {@code id = .}. + * + * @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(); + } + } + } +} diff --git a/jme3-core/src/main/java/com/jme3/opencl/package-info.java b/jme3-core/src/main/java/com/jme3/opencl/package-info.java new file mode 100644 index 000000000..c96a026d1 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/opencl/package-info.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * This package contains an API for using OpenCL together with jME3. + *

+ * Activation:
+ * 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}. + * + *

+ * First steps:
+ * 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. + *
+ * Programs and Kernels: + * 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. + *
+ * Buffers and Images: + * 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. + *
+ * Events: + * 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. + *
+ * 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. + * + *

+ * Interoperability between OpenCL and jME3:
+ * This Wrapper allows to share jME3 Images and VertexBuffers with OpenCL.
+ * {@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.
+ * {@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. + *
+ * Important: 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. + * + *

+ * Experts: choosing the right platform and devices
+ * 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}. + * + *

+ * Exception handling:
+ * 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: + *

    + *
  • {@code NullPointerException}: one of the arguments is {@code null} and + * {@code null} is not allowed
  • + *
  • {@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
  • + *
  • {@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.
  • + *
  • {@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. + *
+ */ +package com.jme3.opencl; + +//TODO: add profiling to Kernel, CommandQueue diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java index 70d25ccd4..1a8270435 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java @@ -35,6 +35,7 @@ import com.jme3.material.RenderState; import com.jme3.material.RenderState.StencilOperation; import com.jme3.material.RenderState.TestFunction; import com.jme3.math.*; +import com.jme3.opencl.OpenCLObjectManager; import com.jme3.renderer.*; import com.jme3.scene.Mesh; import com.jme3.scene.Mesh.Mode; @@ -552,6 +553,7 @@ public final class GLRenderer implements Renderer { public void cleanup() { logger.log(Level.FINE, "Deleting objects and invalidating state"); objManager.deleteAllObjects(this); + OpenCLObjectManager.getInstance().deleteAllObjects(); statistics.clearMemory(); invalidateState(); } @@ -935,6 +937,7 @@ public final class GLRenderer implements Renderer { public void postFrame() { objManager.deleteUnused(this); + OpenCLObjectManager.getInstance().deleteUnusedObjects(); gl.resetStats(); } diff --git a/jme3-core/src/main/java/com/jme3/system/AppSettings.java b/jme3-core/src/main/java/com/jme3/system/AppSettings.java index 05ee2b4bd..4fc23a32e 100644 --- a/jme3-core/src/main/java/com/jme3/system/AppSettings.java +++ b/jme3-core/src/main/java/com/jme3/system/AppSettings.java @@ -31,6 +31,8 @@ */ package com.jme3.system; +import com.jme3.opencl.DefaultPlatformChooser; +import com.jme3.opencl.PlatformChooser; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -160,7 +162,9 @@ public final class AppSettings extends HashMap { 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 { 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 chooser) { + putString("OpenCLPlatformChooser", chooser.getName()); + } + + public String getOpenCLPlatformChooser() { + return getString("OpenCLPlatformChooser"); + } } diff --git a/jme3-core/src/main/java/com/jme3/system/JmeContext.java b/jme3-core/src/main/java/com/jme3/system/JmeContext.java index f4f47ae67..36aaa44a8 100644 --- a/jme3-core/src/main/java/com/jme3/system/JmeContext.java +++ b/jme3-core/src/main/java/com/jme3/system/JmeContext.java @@ -110,6 +110,11 @@ public interface JmeContext { */ public Renderer getRenderer(); + /** + * @return The OpenCL context if available. + */ + public com.jme3.opencl.Context getOpenCLContext(); + /** * @return Mouse input implementation. May be null if not available. */ diff --git a/jme3-core/src/main/java/com/jme3/system/NullContext.java b/jme3-core/src/main/java/com/jme3/system/NullContext.java index 54ee59c42..e7969c89e 100644 --- a/jme3-core/src/main/java/com/jme3/system/NullContext.java +++ b/jme3-core/src/main/java/com/jme3/system/NullContext.java @@ -37,6 +37,7 @@ import com.jme3.input.MouseInput; import com.jme3.input.TouchInput; import com.jme3.input.dummy.DummyKeyInput; import com.jme3.input.dummy.DummyMouseInput; +import com.jme3.opencl.Context; import com.jme3.renderer.Renderer; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; @@ -229,4 +230,9 @@ public class NullContext implements JmeContext, Runnable { return true; // Doesn't really matter if true or false. Either way // RenderManager won't render anything. } + + @Override + public Context getOpenCLContext() { + return null; + } } diff --git a/jme3-core/src/main/resources/Common/OpenCL/Matrix3f.clh b/jme3-core/src/main/resources/Common/OpenCL/Matrix3f.clh new file mode 100644 index 000000000..3888a8e74 --- /dev/null +++ b/jme3-core/src/main/resources/Common/OpenCL/Matrix3f.clh @@ -0,0 +1,224 @@ + +#ifndef MATRIX3_H +#define MATRIX3_H + +//Simple matrix library. +//A 3x3 matrix is represented as a float16 in row major order + +typedef float16 mat3; + +//All matrix functions are prefixed with mat3 or mat4 + +//Returns the zero matrix +inline mat3 mat3Zero() { + return (float16)(0); +} + +//Returns the identity matrix +inline mat3 mat3Identity() { + return (float16) + (1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); +} + +inline mat3 mat3FromRows(float3 row1, float3 row2, float3 row3) { + return (float16) + (row1.x, row1.y, row1.z, 0, + row2.x, row2.y, row2.z, 0, + row3.x, row3.y, row3.z, 0, + 0, 0, 0, 1); +} + +inline mat3 mat3FromColumns(float3 col1, float3 col2, float3 col3) { + return (float16) + (col1.x, col2.x, col3.x, 0, + col1.y, col2.y, col3.y, 0, + col1.z, col2.z, col3.z, 0, + 0, 0, 0, 1); +} + +inline mat3 mat3FromDiagonal(float3 diag) { + return (float16) + (diag.x, 0, 0, 0, + 0, diag.y, 0, 0, + 0, 0, diag.z, 0, + 0, 0, 0, 1); +} + +//Returns the i-th row (0-based) +inline float3 mat3GetRow(mat3 mat, int i) { + if (i==0) return mat.s012; + else if (i==1) return mat.s456; + else return mat.s89a; +} + +//Sets the i-th row (0-based) +inline mat3 mat3SetRow(mat3 mat, int i, float3 row) { + if (i==0) mat.s012 = row; + else if (i==1) mat.s456 = row; + else mat.s89a = row; + return mat; +} + +//Returns the i-th column (0-based) +inline float3 mat3GetColumn(mat3 mat, int i) { + if (i==0) return mat.s048; + else if (i==1) return mat.s159; + else return mat.s26a; +} + +//Sets the i-th column (0-based) +inline mat3 mat3SetColumn(mat3 mat, int i, float3 col) { + if (i==0) mat.s048 = col; + else if (i==1) mat.s159 = col; + else mat.s26a = col; + return mat; +} + +//Returns the diagonal +inline float3 mat3GetDiagonal(mat3 mat) { + return mat.s05a; +} + +//Sets the diagonal +inline mat3 mat3SetDiagonal(mat3 mat, float3 diag) { + mat.s05a = diag; + return mat; +} + +mat3 mat3FromAngleNormalAxis(float angle, float3 axis) { + float fCos = cos(angle); + float fSin = sin(angle); + float fOneMinusCos = 1.0f - fCos; + float fX2 = axis.x * axis.x; + float fY2 = axis.y * axis.y; + float fZ2 = axis.z * axis.z; + float fXYM = axis.x * axis.y * fOneMinusCos; + float fXZM = axis.x * axis.z * fOneMinusCos; + float fYZM = axis.y * axis.z * fOneMinusCos; + float fXSin = axis.x * fSin; + float fYSin = axis.y * fSin; + float fZSin = axis.z * fSin; + + return (float16) ( + fX2 * fOneMinusCos + fCos, + fXYM - fZSin, + fXZM + fYSin, + 0, + fXYM + fZSin, + fY2 * fOneMinusCos + fCos, + fYZM - fXSin, + 0, + fXZM - fYSin, + fYZM + fXSin, + fZ2 * fOneMinusCos + fCos, + 0, + 0, 0, 0, 1 + ); +} + +mat3 mat3FromAngleAxis(float angle, float3 axis) { + return mat3FromAngleNormalAxis(angle, normalize(axis)); +} + +//Multiplies the two matrices A and B +inline mat3 mat3Mult(mat3 A, mat3 B) { + return (float16) ( + dot(A.s012, B.s048), + dot(A.s012, B.s159), + dot(A.s012, B.s26a), + 0, + dot(A.s456, B.s048), + dot(A.s456, B.s159), + dot(A.s456, B.s26a), + 0, + dot(A.s89a, B.s048), + dot(A.s89a, B.s159), + dot(A.s89a, B.s26a), + 0, + 0, 0, 0, 1 + ); +} + +//Computes Av (right multiply of a vector to a matrix) +inline float3 mat3VMult(mat3 A, float3 v) { + return (float3) ( + dot(A.s012, v), + dot(A.s456, v), + dot(A.s89a, v)); +} + +//Computes vA (left multiply of a vector to a matrix) +inline float3 mat3VMult2(float3 v, mat3 A) { + return (float3) ( + dot(v, A.s048), + dot(v, A.s159), + dot(v, A.s26a)); +} + +//Scales this matrix by a constant +inline mat3 mat3Scale(mat3 mat, float s) { + return s*mat; +} + +//Transposes this matrix +inline mat3 mat3Transpose(mat3 mat) { + return mat.s048c159d26ae37bf; //magic +} + +//Computes the determinant +inline float mat3Determinant(mat3 mat) { + float fCo00 = mat.s5 * mat.sa - mat.s6 * mat.s9; + float fCo10 = mat.s6 * mat.s8 - mat.s4 * mat.sa; + float fCo20 = mat.s4 * mat.s9 - mat.s5 * mat.s8; + float fDet = mat.s0 * fCo00 + mat.s1 * fCo10 + mat.s2 * fCo20; + return fDet; +} + +//Creates the adjoint +inline mat3 mat3Adjoint(mat3 mat) { + return (float16) ( + mat.s5 * mat.sa - mat.s6 * mat.s9, + mat.s2 * mat.s9 - mat.s1 * mat.sa, + mat.s1 * mat.s6 - mat.s2 * mat.s5, + 0, + mat.s6 * mat.s8 - mat.s4 * mat.sa, + mat.s0 * mat.sa - mat.s2 * mat.s8, + mat.s2 * mat.s4 - mat.s0 * mat.s6, + 0, + mat.s4 * mat.s9 - mat.s5 * mat.s8, + mat.s1 * mat.s8 - mat.s0 * mat.s9, + mat.s0 * mat.s5 - mat.s1 * mat.s4, + 0, + 0, 0, 0, 1 + ); +} + +//Inverts this matrix +inline mat3 mat3Invert(mat3 mat) { + float det = mat3Determinant(mat); + if (fabs(det) <= 1.1920928955078125E-7f) return mat3Zero(); + mat3 m = mat3Adjoint(mat); + return m / det; +} + +//Computes A+B +inline mat3 mat3Add(mat3 A, mat3 B) { + return A + B; +} + +inline bool mat3Equals(mat3 A, mat3 B, float epsilon) { + return fabs(A.s0 - B.s0)> (48 - bits)); +} + +/** + * Retrieves the next random integer value. + * The buffer used as seed must be read-write. + * Usage: + * + * __kernel void TestRandom(__global ulong* seeds) { + * // ... + * int i = randInt(seeds + get_global_id(0)); + * // --- + * } + * + */ +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: + * + * __kernel void TestRandom(__global ulong* seeds) { + * // ... + * int i = randIntN(n, seeds + get_global_id(0)); + * // --- + * } + * + */ +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: + * + * __kernel void TestRandom(__global ulong* seeds) { + * // ... + * long l = randLong(seeds + get_global_id(0)); + * // --- + * } + * + */ +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: + * + * __kernel void TestRandom(__global ulong* seeds) { + * // ... + * bool b = randBool(seeds + get_global_id(0)); + * // --- + * } + * + */ +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: + * + * __kernel void TestRandom(__global ulong* seeds) { + * // ... + * double d = randDouble(seeds + get_global_id(0)); + * // --- + * } + * + */ +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: + * + * __kernel void TestRandom(__global ulong* seeds) { + * // ... + * float f = randFloat(seeds + get_global_id(0)); + * // --- + * } + * + */ +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: + * + * __kernel void TestRandom(__global ulong* seeds) { + * // ... + * float2 f2 = randGausianf(seeds + get_global_id(0)); + * // --- + * } + * + */ +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: + * + * __kernel void TestRandom(__global ulong* seeds) { + * // ... + * double2 f2 = randGausian(seeds + get_global_id(0)); + * // --- + * } + * + */ +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 \ No newline at end of file diff --git a/jme3-desktop/src/main/java/com/jme3/system/awt/AwtPanelsContext.java b/jme3-desktop/src/main/java/com/jme3/system/awt/AwtPanelsContext.java index 897632a00..ee41ea569 100644 --- a/jme3-desktop/src/main/java/com/jme3/system/awt/AwtPanelsContext.java +++ b/jme3-desktop/src/main/java/com/jme3/system/awt/AwtPanelsContext.java @@ -37,6 +37,7 @@ import com.jme3.input.MouseInput; import com.jme3.input.TouchInput; import com.jme3.input.awt.AwtKeyInput; import com.jme3.input.awt.AwtMouseInput; +import com.jme3.opencl.Context; import com.jme3.renderer.Renderer; import com.jme3.system.*; import java.util.ArrayList; @@ -145,6 +146,11 @@ public class AwtPanelsContext implements JmeContext { return actualContext != null && actualContext.isRenderable(); } + @Override + public Context getOpenCLContext() { + return actualContext.getOpenCLContext(); + } + public AwtPanelsContext(){ } diff --git a/jme3-examples/build.gradle b/jme3-examples/build.gradle index be2e106a6..47ee94c72 100644 --- a/jme3-examples/build.gradle +++ b/jme3-examples/build.gradle @@ -32,6 +32,7 @@ dependencies { compile project(':jme3-jogg') compile project(':jme3-jogl') compile project(':jme3-lwjgl') +// compile project(':jme3-lwjgl3') compile project(':jme3-networking') compile project(':jme3-niftygui') compile project(':jme3-plugins') diff --git a/jme3-examples/src/main/java/jme3test/opencl/HelloOpenCL.java b/jme3-examples/src/main/java/jme3test/opencl/HelloOpenCL.java new file mode 100644 index 000000000..e08802785 --- /dev/null +++ b/jme3-examples/src/main/java/jme3test/opencl/HelloOpenCL.java @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package jme3test.opencl; + +import com.jme3.app.SimpleApplication; +import com.jme3.font.BitmapFont; +import com.jme3.font.BitmapText; +import com.jme3.math.ColorRGBA; +import com.jme3.opencl.*; +import com.jme3.system.AppSettings; +import com.jme3.util.BufferUtils; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.util.Arrays; +import java.util.Objects; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Simple test checking if the basic functions of the OpenCL wrapper work + * @author shaman + */ +public class HelloOpenCL extends SimpleApplication { + private static final Logger LOG = Logger.getLogger(HelloOpenCL.class.getName()); + + public static void main(String[] args){ + HelloOpenCL app = new HelloOpenCL(); + AppSettings settings = new AppSettings(true); + settings.setOpenCLSupport(true); + settings.setVSync(true); +// settings.setRenderer(AppSettings.JOGL_OPENGL_FORWARD_COMPATIBLE); + app.setSettings(settings); + app.start(); // start the game + } + + @Override + public void simpleInitApp() { + BitmapFont fnt = assetManager.loadFont("Interface/Fonts/Default.fnt"); + Context clContext = context.getOpenCLContext(); + if (clContext == null) { + BitmapText txt = new BitmapText(fnt); + txt.setText("No OpenCL Context created!\nSee output log for details."); + txt.setLocalTranslation(5, settings.getHeight() - 5, 0); + guiNode.attachChild(txt); + return; + } + CommandQueue clQueue = clContext.createQueue(); + + StringBuilder str = new StringBuilder(); + str.append("OpenCL Context created:\n Platform: ") + .append(clContext.getDevices().get(0).getPlatform().getName()) + .append("\n Devices: ").append(clContext.getDevices()); + str.append("\nTests:"); + str.append("\n Buffers: ").append(testBuffer(clContext, clQueue)); + str.append("\n Kernel: ").append(testKernel(clContext, clQueue)); + str.append("\n Images: ").append(testImages(clContext, clQueue)); + + clQueue.release(); + + BitmapText txt1 = new BitmapText(fnt); + txt1.setText(str.toString()); + txt1.setLocalTranslation(5, settings.getHeight() - 5, 0); + guiNode.attachChild(txt1); + + flyCam.setEnabled(false); + inputManager.setCursorVisible(true); + } + + private static void assertEquals(byte expected, byte actual, String message) { + if (expected != actual) { + System.err.println(message+": expected="+expected+", actual="+actual); + throw new AssertionError(); + } + } + private static void assertEquals(long expected, long actual, String message) { + if (expected != actual) { + System.err.println(message+": expected="+expected+", actual="+actual); + throw new AssertionError(); + } + } + private static void assertEquals(double expected, double actual, String message) { + if (Math.abs(expected - actual) >= 0.00001) { + System.err.println(message+": expected="+expected+", actual="+actual); + throw new AssertionError(); + } + } + private static void assertEquals(Object expected, Object actual, String message) { + if (!Objects.equals(expected, actual)) { + System.err.println(message+": expected="+expected+", actual="+actual); + throw new AssertionError(); + } + } + + private boolean testBuffer(Context clContext, CommandQueue clQueue) { + try { + //create two buffers + ByteBuffer h1 = BufferUtils.createByteBuffer(256); + Buffer b1 = clContext.createBuffer(256); + ByteBuffer h2 = BufferUtils.createByteBuffer(256); + Buffer b2 = clContext.createBuffer(256); + + //fill buffer + h2.rewind(); + for (int i=0; i<256; ++i) { + h2.put((byte)i); + } + h2.rewind(); + b2.write(clQueue, h2); + + //copy b2 to b1 + b2.copyTo(clQueue, b1); + + //read buffer + h1.rewind(); + b1.read(clQueue, h1); + h1.rewind(); + for (int i=0; i<256; ++i) { + byte b = h1.get(); + assertEquals((byte) i, b, "Wrong byte read"); + } + + //read buffer with offset + int low = 26; + int high = 184; + h1.position(5); + Event event = b1.readAsync(clQueue, h1, high-low, low); + event.waitForFinished(); + h1.position(5); + for (int i=0; i platformListBox; + private ListBox deviceListBox; + + private static String selectedPlatform; + private static String selectedDevice; + private Context clContext; + private static List 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 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 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 chooseDevices(List 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); + } + + } +} diff --git a/jme3-examples/src/main/java/jme3test/opencl/TestMultipleApplications.java b/jme3-examples/src/main/java/jme3test/opencl/TestMultipleApplications.java new file mode 100644 index 000000000..1c2a5bf45 --- /dev/null +++ b/jme3-examples/src/main/java/jme3test/opencl/TestMultipleApplications.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package jme3test.opencl; + +import com.jme3.app.SimpleApplication; +import com.jme3.font.BitmapFont; +import com.jme3.font.BitmapText; +import com.jme3.font.Rectangle; +import com.jme3.opencl.*; +import com.jme3.system.AppSettings; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * This class creates multiple instances of {@link TestVertexBufferSharing}. + * This is used to test if multiple opencl instances can run in parallel. + * @author Sebastian Weiss + */ +public class TestMultipleApplications extends SimpleApplication { + private static final Logger LOG = Logger.getLogger(TestMultipleApplications.class.getName()); + + private static final Object sync = new Object(); + private static Platform selectedPlatform; + private static List 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 chooseDevices(List 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"); + } + } + } +} diff --git a/jme3-examples/src/main/java/jme3test/opencl/TestOpenCLLibraries.java b/jme3-examples/src/main/java/jme3test/opencl/TestOpenCLLibraries.java new file mode 100644 index 000000000..396411397 --- /dev/null +++ b/jme3-examples/src/main/java/jme3test/opencl/TestOpenCLLibraries.java @@ -0,0 +1,403 @@ +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package jme3test.opencl; + +import com.jme3.app.SimpleApplication; +import com.jme3.font.BitmapFont; +import com.jme3.font.BitmapText; +import com.jme3.math.ColorRGBA; +import com.jme3.math.FastMath; +import com.jme3.math.Matrix3f; +import com.jme3.math.Matrix4f; +import com.jme3.opencl.*; +import com.jme3.system.AppSettings; +import com.jme3.util.BufferUtils; +import java.nio.*; +import java.util.Arrays; +import java.util.Objects; +import java.util.Random; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Test class for the build in libraries + * @author shaman + */ +public class TestOpenCLLibraries extends SimpleApplication { + private static final Logger LOG = Logger.getLogger(TestOpenCLLibraries.class.getName()); + + public static void main(String[] args){ + TestOpenCLLibraries app = new TestOpenCLLibraries(); + AppSettings settings = new AppSettings(true); + settings.setOpenCLSupport(true); + settings.setVSync(true); +// settings.setRenderer(AppSettings.JOGL_OPENGL_FORWARD_COMPATIBLE); + app.setSettings(settings); + app.start(); // start the game + } + + @Override + public void simpleInitApp() { + BitmapFont fnt = assetManager.loadFont("Interface/Fonts/Default.fnt"); + Context clContext = context.getOpenCLContext(); + if (clContext == null) { + BitmapText txt = new BitmapText(fnt); + txt.setText("No OpenCL Context created!\nSee output log for details."); + txt.setLocalTranslation(5, settings.getHeight() - 5, 0); + guiNode.attachChild(txt); + return; + } + CommandQueue clQueue = clContext.createQueue(clContext.getDevices().get(0)); + + StringBuilder str = new StringBuilder(); + str.append("OpenCL Context created:\n Platform: ") + .append(clContext.getDevices().get(0).getPlatform().getName()) + .append("\n Devices: ").append(clContext.getDevices()); + str.append("\nTests:"); + str.append("\n Random numbers: ").append(testRandom(clContext, clQueue)); + str.append("\n Matrix3f: ").append(testMatrix3f(clContext, clQueue)); + str.append("\n Matrix4f: ").append(testMatrix4f(clContext, clQueue)); + + clQueue.release(); + + BitmapText txt1 = new BitmapText(fnt); + txt1.setText(str.toString()); + txt1.setLocalTranslation(5, settings.getHeight() - 5, 0); + guiNode.attachChild(txt1); + + flyCam.setEnabled(false); + inputManager.setCursorVisible(true); + } + + private static void assertEquals(byte expected, byte actual, String message) { + if (expected != actual) { + System.err.println(message+": expected="+expected+", actual="+actual); + throw new AssertionError(); + } + } + private static void assertEquals(long expected, long actual, String message) { + if (expected != actual) { + System.err.println(message+": expected="+expected+", actual="+actual); + throw new AssertionError(); + } + } + private static void assertEquals(double expected, double actual, String message) { + if (Math.abs(expected - actual) >= 0.00001) { + System.err.println(message+": expected="+expected+", actual="+actual); + throw new AssertionError(); + } + } + private static void assertEquals(Object expected, Object actual, String message) { + if (!Objects.equals(expected, actual)) { + System.err.println(message+": expected="+expected+", actual="+actual); + throw new AssertionError(); + } + } + + private boolean testRandom(Context clContext, CommandQueue clQueue) { + try { + //test for doubles + boolean supportsDoubles = clContext.getDevices().get(0).hasDouble(); + + //create code + String code = "" + + "#import \"Common/OpenCL/Random.clh\"\n" + + "__kernel void TestBool(__global ulong* seeds, __global uchar* results) {\n" + + " results[get_global_id(0)] = randBool(seeds + get_global_id(0)) ? 1 : 0;\n" + + "}\n" + + "__kernel void TestInt(__global ulong* seeds, __global int* results) {\n" + + " results[get_global_id(0)] = randInt(seeds + get_global_id(0));\n" + + "}\n" + + "__kernel void TestIntN(__global ulong* seeds, int n, __global int* results) {\n" + + " results[get_global_id(0)] = randIntN(n, seeds + get_global_id(0));\n" + + "}\n" + + "__kernel void TestLong(__global ulong* seeds, __global long* results) {\n" + + " results[get_global_id(0)] = randLong(seeds + get_global_id(0));\n" + + "}\n" + + "__kernel void TestFloat(__global ulong* seeds, __global float* results) {\n" + + " results[get_global_id(0)] = randFloat(seeds + get_global_id(0));\n" + + "}\n" + + "#ifdef RANDOM_DOUBLES\n" + + "__kernel void TestDouble(__global ulong* seeds, __global double* results) {\n" + + " results[get_global_id(0)] = randDouble(seeds + get_global_id(0));\n" + + "}\n" + + "#endif\n"; + if (supportsDoubles) { + code = "#define RANDOM_DOUBLES\n" + code; + } + Program program = clContext.createProgramFromSourceCodeWithDependencies(code, assetManager); + program.build(); + + int count = 256; + Kernel.WorkSize ws = new Kernel.WorkSize(count); + + //create seeds + Random initRandom = new Random(); + long[] seeds = new long[count]; + Random[] randoms = new Random[count]; + for (int i=0; i + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jme3-examples/src/main/resources/jme3test/opencl/JuliaSet.cl b/jme3-examples/src/main/resources/jme3test/opencl/JuliaSet.cl new file mode 100644 index 000000000..d2a42d338 --- /dev/null +++ b/jme3-examples/src/main/resources/jme3test/opencl/JuliaSet.cl @@ -0,0 +1,99 @@ + + +//2 component vector to hold the real and imaginary parts of a complex number: +typedef float2 cfloat; + +#define I ((cfloat)(0.0, 1.0)) + +inline float real(cfloat a){ + return a.x; +} +inline float imag(cfloat a){ + return a.y; +} + +inline float cmod(cfloat a){ + return (sqrt(a.x*a.x + a.y*a.y)); +} + +inline cfloat cadd(cfloat a, cfloat b){ + return (cfloat)( a.x + b.x, a.y + b.y); +} + +inline float carg(cfloat a){ + if(a.x > 0){ + return atan(a.y / a.x); + + }else if(a.x < 0 && a.y >= 0){ + return atan(a.y / a.x) + M_PI_F; + + }else if(a.x < 0 && a.y < 0){ + return atan(a.y / a.x) - M_PI_F; + + }else if(a.x == 0 && a.y > 0){ + return M_PI_F/2; + + }else if(a.x == 0 && a.y < 0){ + return -M_PI_F/2; + + }else{ + return 0; + } +} + +inline cfloat cmult(cfloat a, cfloat b){ + return (cfloat)( a.x*b.x - a.y*b.y, a.x*b.y + a.y*b.x); +} + +inline cfloat csqrt(cfloat a){ + return (cfloat)( sqrt(cmod(a)) * cos(carg(a)/2), sqrt(cmod(a)) * sin(carg(a)/2)); +} + +inline float4 getColor(int iteration, int numIterations) { + //color transition: black -> red -> blue -> white + int step = numIterations / 2; + if (iteration < step) { + return mix( (float4)(0,0,0,1), (float4)(1,0,0,1), iteration / (float) step); + } else { + return mix( (float4)(1,0,0,1), (float4)(0,0,1,1), (iteration-step) / (float) (numIterations - step)); + } +} + +__kernel void JuliaSet(write_only image2d_t outputImage, const cfloat C, int numIterations) +{ + // get id of element in array + int x = get_global_id(0); + int y = get_global_id(1); + int w = get_global_size(0); + int h = get_global_size(1); + + cfloat Z = { ( -w / 2 + x) / (w/4.0f) , ( -h / 2 + y) / (h/4.0f) }; + int iteration = 0; + + while (iteration < numIterations) + { + cfloat Zpow2 = cmult(Z, Z); + cfloat Zn = cadd(Zpow2, C); + Z.x = Zn.x; + Z.y = Zn.y; + iteration++; + if(cmod(Z) > 2) + { + break; + } + } + + float4 color; + + // threshold reached mark pixel as white + if (iteration == numIterations) + { + color = (float4)(1,1,1,1); + } + else + { + color = getColor(iteration, numIterations); + } + + write_imagef(outputImage, (int2)(x, y), color); +} \ No newline at end of file diff --git a/jme3-ios/src/main/java/com/jme3/system/ios/IGLESContext.java b/jme3-ios/src/main/java/com/jme3/system/ios/IGLESContext.java index 2d046550e..14ab252a5 100644 --- a/jme3-ios/src/main/java/com/jme3/system/ios/IGLESContext.java +++ b/jme3-ios/src/main/java/com/jme3/system/ios/IGLESContext.java @@ -36,6 +36,7 @@ import com.jme3.input.dummy.DummyKeyInput; import com.jme3.input.dummy.DummyMouseInput; import com.jme3.system.*; import com.jme3.input.ios.IosInputHandler; +import com.jme3.opencl.Context; import com.jme3.renderer.ios.IosGL; import com.jme3.renderer.opengl.GL; import com.jme3.renderer.opengl.GLDebugES; @@ -212,4 +213,10 @@ public class IGLESContext implements JmeContext { } } } + + @Override + public Context getOpenCLContext() { + logger.warning("OpenCL not yet supported on this platform"); + return null; + } } \ No newline at end of file diff --git a/jme3-jogl/build.gradle b/jme3-jogl/build.gradle index df24c15ec..b71be027a 100644 --- a/jme3-jogl/build.gradle +++ b/jme3-jogl/build.gradle @@ -8,4 +8,5 @@ dependencies { compile 'org.jogamp.gluegen:gluegen-rt-main:2.3.2' compile 'org.jogamp.jogl:jogl-all-main:2.3.2' compile 'org.jogamp.joal:joal-main:2.3.2' + compile 'org.jogamp.jocl:jocl-main:2.3.2' } diff --git a/jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclBuffer.java b/jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclBuffer.java new file mode 100644 index 000000000..1085ad7d2 --- /dev/null +++ b/jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclBuffer.java @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl.jocl; + +import com.jme3.opencl.*; +import java.nio.ByteBuffer; +import com.jogamp.opencl.*; +import com.jogamp.opencl.llb.CL; +import com.jogamp.opencl.llb.gl.CLGL; +import java.util.EnumSet; + +/** + * + * @author shaman + */ +public class JoclBuffer extends Buffer { + + final long id; + final CL cl; + + public JoclBuffer(long id) { + super(new ReleaserImpl(id)); + this.id = id; + this.cl = CLPlatform.getLowLevelCLInterface(); + } + + @Override + public long getSize() { + Utils.pointers[0].rewind(); + int ret = cl.clGetMemObjectInfo(id, CL.CL_MEM_SIZE, Utils.pointers[0].elementSize(), Utils.pointers[0].getBuffer(), null); + Utils.checkError(ret, "clGetMemObjectInfo"); + return Utils.pointers[0].get(); + } + + @Override + public MemoryAccess getMemoryAccessFlags() { + Utils.pointers[0].rewind(); + int ret = cl.clGetMemObjectInfo(id, CL.CL_MEM_TYPE, Utils.pointers[0].elementSize(), Utils.pointers[0].getBuffer(), null); + Utils.checkError(ret, "clGetMemObjectInfo"); + long flags = Utils.pointers[0].get(); + return Utils.getMemoryAccessFromFlag(flags); + } + + @Override + public void read(CommandQueue queue, ByteBuffer dest, long size, long offset) { + long q = ((JoclCommandQueue) queue).id; + int ret = cl.clEnqueueReadBuffer(q, id, CL.CL_TRUE, offset, size, dest, 0, null, null); + Utils.checkError(ret, "clEnqueueReadBuffer"); + } + + @Override + public Event readAsync(CommandQueue queue, ByteBuffer dest, long size, long offset) { + Utils.pointers[0].rewind(); + long q = ((JoclCommandQueue) queue).id; + int ret = cl.clEnqueueReadBuffer(q, id, CL.CL_FALSE, offset, size, dest, 0, null, Utils.pointers[0]); + Utils.checkError(ret, "clEnqueueReadBuffer"); + long event = Utils.pointers[0].get(0); + return new JoclEvent(event); + } + + @Override + public void write(CommandQueue queue, ByteBuffer src, long size, long offset) { + long q = ((JoclCommandQueue)queue).id; + int ret = cl.clEnqueueWriteBuffer(q, id, CL.CL_TRUE, offset, size, src, 0, null, null); + Utils.checkError(ret, "clEnqueueWriteBuffer"); + } + + @Override + public Event writeAsync(CommandQueue queue, ByteBuffer src, long size, long offset) { + Utils.pointers[0].rewind(); + long q = ((JoclCommandQueue)queue).id; + int ret = cl.clEnqueueWriteBuffer(q, id, CL.CL_FALSE, offset, size, src, 0, null, Utils.pointers[0]); + Utils.checkError(ret, "clEnqueueWriteBuffer"); + long event = Utils.pointers[0].get(0); + return new JoclEvent(event); + } + + @Override + public void copyTo(CommandQueue queue, Buffer dest, long size, long srcOffset, long destOffset) { + Utils.pointers[0].rewind(); + long q = ((JoclCommandQueue)queue).id; + long did = ((JoclBuffer) dest).id; + int ret = cl.clEnqueueCopyBuffer(q, id, did, srcOffset, destOffset, size, 0, null, Utils.pointers[0]); + Utils.checkError(ret, "clEnqueueCopyBuffer"); + ret = cl.clWaitForEvents(1, Utils.pointers[0]); + Utils.checkError(ret, "clWaitForEvents"); + } + + @Override + public Event copyToAsync(CommandQueue queue, Buffer dest, long size, long srcOffset, long destOffset) { + Utils.pointers[0].rewind(); + long q = ((JoclCommandQueue)queue).id; + long did = ((JoclBuffer) dest).id; + int ret = cl.clEnqueueCopyBuffer(q, id, did, srcOffset, destOffset, size, 0, null, Utils.pointers[0]); + Utils.checkError(ret, "clEnqueueCopyBuffer"); + long event = Utils.pointers[0].get(0); + return new JoclEvent(event); + } + + @Override + public ByteBuffer map(CommandQueue queue, long size, long offset, MappingAccess access) { + long q = ((JoclCommandQueue)queue).id; + Utils.errorBuffer.rewind(); + long flags = Utils.getMappingAccessFlags(access); + ByteBuffer b = cl.clEnqueueMapBuffer(q, id, CL.CL_TRUE, flags, offset, size, 0, null, null, Utils.errorBuffer); + Utils.checkError(Utils.errorBuffer, "clEnqueueMapBuffer"); + return b; + } + + @Override + public void unmap(CommandQueue queue, ByteBuffer ptr) { + long q = ((JoclCommandQueue)queue).id; + Utils.pointers[0].rewind(); + ptr.position(0); + int ret = cl.clEnqueueUnmapMemObject(q, id, ptr, 0, null, Utils.pointers[0]); + Utils.checkError(ret, "clEnqueueUnmapMemObject"); + ret = cl.clWaitForEvents(1, Utils.pointers[0]); + Utils.checkError(ret, "clWaitForEvents"); + } + + @Override + public com.jme3.opencl.Buffer.AsyncMapping mapAsync(CommandQueue queue, long size, long offset, MappingAccess access) { + long q = ((JoclCommandQueue)queue).id; + Utils.pointers[0].rewind(); + Utils.errorBuffer.rewind(); + long flags = Utils.getMappingAccessFlags(access); + ByteBuffer b = cl.clEnqueueMapBuffer(q, id, CL.CL_FALSE, flags, offset, size, 0, null, Utils.pointers[0], Utils.errorBuffer); + Utils.checkError(Utils.errorBuffer, "clEnqueueMapBuffer"); + long event = Utils.pointers[0].get(0); + return new com.jme3.opencl.Buffer.AsyncMapping(new JoclEvent(event), b); + } + + @Override + public Event fillAsync(CommandQueue queue, ByteBuffer pattern, long size, long offset) { + throw new UnsupportedOperationException("Not supported by Jocl!"); + } + + @Override + public Event copyToImageAsync(CommandQueue queue, Image dest, long srcOffset, long[] destOrigin, long[] destRegion) { + if (destOrigin.length!=3 || destRegion.length!=3) { + throw new IllegalArgumentException("origin and region must both be arrays of length 3"); + } + Utils.pointers[0].rewind(); + Utils.pointers[1].rewind(); + Utils.pointers[2].rewind(); + Utils.pointers[1].put(destOrigin[0]).put(destOrigin[1]).put(destOrigin[2]).position(0); + Utils.pointers[2].put(destRegion[0]).put(destRegion[1]).put(destRegion[2]).position(0); + long q = ((JoclCommandQueue)queue).id; + long i = ((JoclImage) dest).id; + int ret = cl.clEnqueueCopyBufferToImage(q, id, i, srcOffset, Utils.pointers[1], Utils.pointers[2], 0, null, Utils.pointers[0]); + Utils.checkError(ret, "clEnqueueCopyBufferToImage"); + long event = Utils.pointers[0].get(0); + return new JoclEvent(event); + } + + @Override + public Event acquireBufferForSharingAsync(CommandQueue queue) { + Utils.pointers[0].rewind(); + Utils.pointers[1].rewind(); + Utils.pointers[1].put(0, id); + long q = ((JoclCommandQueue)queue).id; + ((CLGL) cl).clEnqueueAcquireGLObjects(q, 1, Utils.pointers[1], 0, null, Utils.pointers[0]); + long event = Utils.pointers[0].get(0); + return new JoclEvent(event); + } + @Override + public void acquireBufferForSharingNoEvent(CommandQueue queue) { + Utils.pointers[1].rewind(); + Utils.pointers[1].put(0, id); + long q = ((JoclCommandQueue)queue).id; + ((CLGL) cl).clEnqueueAcquireGLObjects(q, 1, Utils.pointers[1], 0, null, null); + } + + @Override + public Event releaseBufferForSharingAsync(CommandQueue queue) { + Utils.pointers[0].rewind(); + Utils.pointers[1].rewind(); + Utils.pointers[1].put(0, id); + long q = ((JoclCommandQueue)queue).id; + ((CLGL) cl).clEnqueueReleaseGLObjects(q, 1, Utils.pointers[1], 0, null, Utils.pointers[0]); + long event = Utils.pointers[0].get(0); + return new JoclEvent(event); + } + @Override + public void releaseBufferForSharingNoEvent(CommandQueue queue) { + Utils.pointers[1].rewind(); + Utils.pointers[1].put(0, id); + long q = ((JoclCommandQueue)queue).id; + ((CLGL) cl).clEnqueueReleaseGLObjects(q, 1, Utils.pointers[1], 0, null, null); + } + + private static class ReleaserImpl implements ObjectReleaser { + private long mem; + private ReleaserImpl(long mem) { + this.mem = mem; + } + @Override + public void release() { + if (mem != 0) { + int ret = CLPlatform.getLowLevelCLInterface().clReleaseMemObject(mem); + mem = 0; + Utils.reportError(ret, "clReleaseMemObject"); + } + } + + } +} diff --git a/jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclCommandQueue.java b/jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclCommandQueue.java new file mode 100644 index 000000000..e7b8a3743 --- /dev/null +++ b/jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclCommandQueue.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl.jocl; + +import com.jme3.opencl.CommandQueue; +import com.jme3.opencl.OpenCLObjectManager; +import com.jogamp.opencl.CLCommandQueue; +import com.jogamp.opencl.CLPlatform; +import com.jogamp.opencl.llb.CL; +import com.jogamp.opencl.llb.CLCommandQueueBinding; + +/** + * + * @author shaman + */ +public class JoclCommandQueue extends CommandQueue { + + final CL cl; + final long id; + + public JoclCommandQueue(long id) { + super(new ReleaserImpl(id)); + this.id = id; + this.cl = CLPlatform.getLowLevelCLInterface(); + } + + @Override + public void flush() { + int ret = cl.clFlush(id); + Utils.checkError(ret, "clFlush"); + } + + @Override + public void finish() { + int ret = cl.clFinish(id); + Utils.checkError(ret, "clFinish"); + } + + private static class ReleaserImpl implements ObjectReleaser { + private long id; + + private ReleaserImpl(long id) { + this.id = id; + } + + @Override + public void release() { + if (id != 0) { + int ret = CLPlatform.getLowLevelCLInterface().clReleaseCommandQueue(id); + id = 0; + Utils.reportError(ret, "clReleaseCommandQueue"); + } + } + } +} diff --git a/jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclContext.java b/jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclContext.java new file mode 100644 index 000000000..d372587f1 --- /dev/null +++ b/jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclContext.java @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl.jocl; + +import com.jme3.opencl.*; +import com.jme3.opencl.Context; +import com.jme3.opencl.Image.ImageDescriptor; +import com.jme3.opencl.Image.ImageFormat; +import com.jme3.scene.VertexBuffer; +import com.jme3.texture.FrameBuffer; +import com.jme3.texture.Texture; +import com.jogamp.opencl.CLContext; +import com.jogamp.opencl.CLImageFormat; +import com.jogamp.opencl.CLMemory.Mem; +import com.jogamp.opencl.CLPlatform; +import com.jogamp.opencl.llb.CL; +import com.jogamp.opencl.llb.gl.CLGL; +import com.jogamp.opencl.llb.impl.CLImageFormatImpl; +import com.jogamp.opengl.GL; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author shaman + */ +public class JoclContext extends Context { + private static final Logger LOG = Logger.getLogger(JoclContext.class.getName()); + + final CLContext context; + final long id; + final CL cl; + private final List devices; + + public JoclContext(CLContext context, List 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 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 devices; + private ReleaserImpl(long id, List 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"); + } + } + + } +} diff --git a/jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclDevice.java b/jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclDevice.java new file mode 100644 index 000000000..d4d9b08e1 --- /dev/null +++ b/jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclDevice.java @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl.jocl; + +import com.jme3.opencl.Device; +import com.jme3.opencl.Platform; +import com.jogamp.opencl.CLDevice; +import java.util.Arrays; +import java.util.Collection; + +/** + * + * @author shaman + */ +public final class JoclDevice implements Device { + + final long id; + final CLDevice device; + final JoclPlatform platform; + + public JoclDevice(CLDevice device, JoclPlatform platform) { + this.id = device.ID; + this.device = device; + this.platform = platform; + } + + public long getId() { + return id; + } + + public CLDevice getDevice() { + return device; + } + + @Override + public JoclPlatform getPlatform() { + return platform; + } + + @Override + public DeviceType getDeviceType() { + CLDevice.Type type = device.getType(); + switch (type) { + case ACCELERATOR: return DeviceType.ACCELEARTOR; + case CPU: return DeviceType.CPU; + case GPU: return DeviceType.GPU; + default: return DeviceType.DEFAULT; + } + } + + @Override + public int getVendorId() { + return (int) device.getVendorID(); + } + + @Override + public boolean isAvailable() { + return device.isAvailable(); + } + + @Override + public boolean hasCompiler() { + return device.isCompilerAvailable(); + } + + @Override + public boolean hasDouble() { + return hasExtension("cl_khr_fp64"); + } + + @Override + public boolean hasHalfFloat() { + return hasExtension("cl_khr_fp16"); + } + + @Override + public boolean hasErrorCorrectingMemory() { + return device.isErrorCorrectionSupported(); + } + + @Override + public boolean hasUnifiedMemory() { + return device.isMemoryUnified(); + } + + @Override + public boolean hasImageSupport() { + return device.isImageSupportAvailable(); + } + + @Override + public boolean hasWritableImage3D() { + return hasExtension("cl_khr_3d_image_writes"); + } + + @Override + public boolean hasOpenGLInterop() { + return hasExtension("cl_khr_gl_sharing"); + } + + @Override + public boolean hasExtension(String extension) { + return getExtensions().contains(extension); + } + + @Override + public Collection 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 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"); + } + } + } +} diff --git a/jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclPlatform.java b/jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclPlatform.java new file mode 100644 index 000000000..56d551180 --- /dev/null +++ b/jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclPlatform.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl.jocl; + +import com.jme3.opencl.Device; +import com.jme3.opencl.Platform; +import com.jogamp.opencl.CLDevice; +import com.jogamp.opencl.CLPlatform; +import com.jogamp.opencl.llb.CL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +/** + * + * @author shaman + */ +public final class JoclPlatform implements Platform { + + final CLPlatform platform; + List devices; + + public JoclPlatform(CLPlatform platform) { + this.platform = platform; + } + + public CLPlatform getPlatform() { + return platform; + } + + @Override + public List 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 getExtensions() { + return platform.getExtensions(); + } + +} diff --git a/jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclProgram.java b/jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclProgram.java new file mode 100644 index 000000000..e64341876 --- /dev/null +++ b/jme3-jogl/src/main/java/com/jme3/opencl/jocl/JoclProgram.java @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl.jocl; + +import com.jme3.opencl.*; +import com.jogamp.common.nio.Buffers; +import com.jogamp.common.nio.PointerBuffer; +import com.jogamp.opencl.CLPlatform; +import com.jogamp.opencl.CLProgram; +import com.jogamp.opencl.llb.CL; +import com.jogamp.opencl.util.CLUtil; +import java.nio.ByteBuffer; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static com.jogamp.common.nio.Buffers.newDirectByteBuffer; +/** + * + * @author shaman + */ +public class JoclProgram extends Program { + private static final Logger LOG = Logger.getLogger(JoclProgram.class.getName()); + + final long program; + final CL cl; + private final JoclContext context; + + public JoclProgram(long program, JoclContext context) { + super(new ReleaserImpl(program)); + this.program = program; + this.context = context; + this.cl = CLPlatform.getLowLevelCLInterface(); + } + + @Override + public void build(String args, Device... devices) throws KernelCompilationException { + PointerBuffer deviceList = null; + int deviceCount = 0; + if (devices != null) { + deviceList = PointerBuffer.allocateDirect(devices.length); + for (Device d : devices) { + deviceList.put(((JoclDevice) d).id); + } + deviceCount = devices.length; + deviceList.rewind(); + } + int ret = cl.clBuildProgram(program, deviceCount, deviceList, args, null); + if (ret != CL.CL_SUCCESS) { + String log = Log(); + LOG.log(Level.WARNING, "Unable to compile program:\n{0}", log); + if (ret == CL.CL_BUILD_PROGRAM_FAILURE) { + throw new KernelCompilationException("Failed to build program", ret, log); + } else { + Utils.checkError(ret, "clBuildProgram"); + } + } else { + LOG.log(Level.INFO, "Program compiled:\n{0}", Log()); + } + } + + private String Log(long device) { + Utils.pointers[0].rewind(); + int ret = cl.clGetProgramBuildInfo(program, device, CL.CL_PROGRAM_BUILD_LOG, 0, null, Utils.pointers[0]); + Utils.checkError(ret, "clGetProgramBuildInfo"); + int count = (int) Utils.pointers[0].get(0); + final ByteBuffer buffer = newDirectByteBuffer(count); + ret = cl.clGetProgramBuildInfo(program, device, CL.CL_PROGRAM_BUILD_LOG, buffer.capacity(), buffer, null); + Utils.checkError(ret, "clGetProgramBuildInfo"); + return CLUtil.clString2JavaString(buffer, count); + } + + private String Log() { + StringBuilder str = new StringBuilder(); + for (JoclDevice device : context.getDevices()) { + long d = device.id; + str.append(device.getName()).append(":\n"); + str.append(Log(d)); + str.append('\n'); + } + return str.toString(); + } + + @Override + public Kernel createKernel(String name) { + Utils.errorBuffer.rewind(); + long kernel = cl.clCreateKernel(program, name, Utils.errorBuffer); + Utils.checkError(Utils.errorBuffer, "clCreateKernel"); + return new JoclKernel(kernel); + } + + @Override + public Kernel[] createAllKernels() { + Utils.tempBuffers[0].b16i.rewind(); + int ret = cl.clCreateKernelsInProgram(program, 0, null, Utils.tempBuffers[0].b16i); + Utils.checkError(ret, "clCreateKernelsInProgram"); + int count = Utils.tempBuffers[0].b16i.get(0); + PointerBuffer buf = PointerBuffer.allocateDirect(count); + ret = cl.clCreateKernelsInProgram(program, count, buf, null); + Utils.checkError(ret, "clCreateKernelsInProgram"); + Kernel[] kx = new Kernel[count]; + for (int i=0; i 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); + } + } + +} diff --git a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglContext.java b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglContext.java index 24a647ef6..12c1d10cb 100644 --- a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglContext.java +++ b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglContext.java @@ -35,6 +35,12 @@ package com.jme3.system.jogl; import com.jme3.input.JoyInput; import com.jme3.input.KeyInput; import com.jme3.input.MouseInput; +import com.jme3.opencl.Context; +import com.jme3.opencl.DefaultPlatformChooser; +import com.jme3.opencl.Device; +import com.jme3.opencl.PlatformChooser; +import com.jme3.opencl.jocl.JoclDevice; +import com.jme3.opencl.jocl.JoclPlatform; import com.jme3.renderer.Renderer; import com.jme3.renderer.RendererException; import com.jme3.renderer.jogl.JoglGL; @@ -55,6 +61,10 @@ import com.jme3.system.JmeContext; import com.jme3.system.NanoTimer; import com.jme3.system.SystemListener; import com.jme3.system.Timer; +import com.jogamp.opencl.CLDevice; +import com.jogamp.opencl.CLPlatform; +import com.jogamp.opencl.gl.CLGLContext; +import com.jogamp.opencl.llb.CL; import java.nio.IntBuffer; import java.util.concurrent.atomic.AtomicBoolean; @@ -64,6 +74,8 @@ import java.util.logging.Logger; import com.jogamp.opengl.GL; import com.jogamp.opengl.GL2GL3; import com.jogamp.opengl.GLContext; +import java.util.ArrayList; +import java.util.List; public abstract class JoglContext implements JmeContext { @@ -84,6 +96,8 @@ public abstract class JoglContext implements JmeContext { protected MouseInput mouseInput; protected JoyInput joyInput; + protected com.jme3.opencl.Context clContext; + @Override public void setSystemListener(SystemListener listener){ this.listener = listener; @@ -209,6 +223,101 @@ public abstract class JoglContext implements JmeContext { if (joyInput != null) { joyInput.initialize(); } + + if (settings.isOpenCLSupport()) { + initOpenCL(); + } + } + + @SuppressWarnings("unchecked") + protected void initOpenCL() { + logger.info("Initialize OpenCL with JOGL"); + + //load platforms and devices + StringBuilder platformInfos = new StringBuilder(); + ArrayList platforms = new ArrayList(); + for (CLPlatform p : CLPlatform.listCLPlatforms()) { + platforms.add(new JoclPlatform(p)); + } + platformInfos.append("Available OpenCL platforms:"); + for (int i=0; i devices = platform.getDevices(); + platformInfos.append("\n * Available devices:"); + for (int j=0; j choosenDevices = chooser.chooseDevices(platforms); + List 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) 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; + } + } diff --git a/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglBuffer.java b/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglBuffer.java new file mode 100644 index 000000000..cd6cdeb27 --- /dev/null +++ b/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglBuffer.java @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl.lwjgl; + +import com.jme3.opencl.*; +import java.nio.ByteBuffer; +import org.lwjgl.opencl.*; + +/** + * + * @author shaman + */ +public class LwjglBuffer extends Buffer { + + private final CLMem buffer; + + public LwjglBuffer(CLMem buffer) { + super(new ReleaserImpl(buffer)); + this.buffer = buffer; + } + public CLMem getBuffer() { + return buffer; + } + + @Override + public long getSize() { + return buffer.getInfoSize(CL10.CL_MEM_SIZE); + } + + @Override + public MemoryAccess getMemoryAccessFlags() { + return Utils.getMemoryAccessFromFlag(buffer.getInfoLong(CL10.CL_MEM_FLAGS)); + } + + @Override + public void read(CommandQueue queue, ByteBuffer dest, long size, long offset) { + //Note: LWJGL does not support the size parameter, I have to set the buffer limit + dest.limit((int) (dest.position() + size)); + int ret = CL10.clEnqueueReadBuffer(((LwjglCommandQueue)queue).getQueue(), + buffer, CL10.CL_TRUE, offset, dest, null, null); + Utils.checkError(ret, "clEnqueueReadBuffer"); + } + + @Override + public Event readAsync(CommandQueue queue, ByteBuffer dest, long size, long offset) { + //Note: LWJGL does not support the size parameter, I have to set the buffer limit + dest.limit((int) (dest.position() + size)); + Utils.pointerBuffers[0].rewind(); + CLCommandQueue q = ((LwjglCommandQueue)queue).getQueue(); + int ret = CL10.clEnqueueReadBuffer(q, buffer, CL10.CL_FALSE, offset, dest, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueReadBuffer"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(q.getCLEvent(event)); + } + + @Override + public void write(CommandQueue queue, ByteBuffer src, long size, long offset) { + //Note: LWJGL does not support the size parameter, I have to set the buffer limit + src.limit((int) (src.position() + size)); + CLCommandQueue q = ((LwjglCommandQueue)queue).getQueue(); + int ret = CL10.clEnqueueWriteBuffer(q, buffer, CL10.CL_TRUE, offset, src, null, null); + Utils.checkError(ret, "clEnqueueWriteBuffer"); + } + + @Override + public Event writeAsync(CommandQueue queue, ByteBuffer src, long size, long offset) { + //Note: LWJGL does not support the size parameter, I have to set the buffer limit + src.limit((int) (src.position() + size)); + Utils.pointerBuffers[0].rewind(); + CLCommandQueue q = ((LwjglCommandQueue)queue).getQueue(); + int ret = CL10.clEnqueueWriteBuffer(q, buffer, CL10.CL_FALSE, offset, src, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueWriteBuffer"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(q.getCLEvent(event)); + } + + @Override + public void copyTo(CommandQueue queue, Buffer dest, long size, long srcOffset, long destOffset) { + CLCommandQueue q = ((LwjglCommandQueue)queue).getQueue(); + Utils.pointerBuffers[0].rewind(); + int ret = CL10.clEnqueueCopyBuffer(q, buffer, ((LwjglBuffer) dest).buffer, srcOffset, destOffset, size, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueCopyBuffer"); + long event = Utils.pointerBuffers[0].get(0); + ret = CL10.clWaitForEvents(q.getCLEvent(event)); + Utils.checkError(ret, "clWaitForEvents"); + } + + @Override + public Event copyToAsync(CommandQueue queue, Buffer dest, long size, long srcOffset, long destOffset) { + CLCommandQueue q = ((LwjglCommandQueue)queue).getQueue(); + Utils.pointerBuffers[0].rewind(); + int ret = CL10.clEnqueueCopyBuffer(q, buffer, ((LwjglBuffer) dest).buffer, srcOffset, destOffset, size, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueCopyBuffer"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(q.getCLEvent(event)); + } + + @Override + public ByteBuffer map(CommandQueue queue, long size, long offset, MappingAccess access) { + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + long flags = Utils.getMappingAccessFlags(access); + Utils.errorBuffer.rewind(); + ByteBuffer b = CL10.clEnqueueMapBuffer(q, buffer, CL10.CL_TRUE, flags, offset, size, null, null, Utils.errorBuffer); + Utils.checkError(Utils.errorBuffer, "clEnqueueMapBuffer"); + return b; + } + + @Override + public void unmap(CommandQueue queue, ByteBuffer ptr) { + ptr.position(0); + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + Utils.pointerBuffers[0].rewind(); + int ret = CL10.clEnqueueUnmapMemObject(q, buffer, ptr, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueUnmapMemObject"); + long event = Utils.pointerBuffers[0].get(0); + ret = CL10.clWaitForEvents(q.getCLEvent(event)); + Utils.checkError(ret, "clWaitForEvents"); + } + + @Override + public com.jme3.opencl.Buffer.AsyncMapping mapAsync(CommandQueue queue, long size, long offset, MappingAccess access) { + Utils.pointerBuffers[0].rewind(); + Utils.errorBuffer.rewind(); + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + long flags = Utils.getMappingAccessFlags(access); + ByteBuffer buf = CL10.clEnqueueMapBuffer(q, buffer, CL10.CL_FALSE, flags, offset, size, null, Utils.pointerBuffers[0], Utils.errorBuffer); + Utils.checkError(Utils.errorBuffer, "clEnqueueMapBuffer"); + long event = Utils.pointerBuffers[0].get(0); + return new com.jme3.opencl.Buffer.AsyncMapping(new LwjglEvent(q.getCLEvent(event)), buf); + } + + @Override + public Event fillAsync(CommandQueue queue, ByteBuffer pattern, long size, long offset) { + Utils.pointerBuffers[0].rewind(); + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL12.clEnqueueFillBuffer(q, buffer, pattern, offset, size, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueFillBuffer"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(q.getCLEvent(event)); + } + + @Override + public Event copyToImageAsync(CommandQueue queue, Image dest, long srcOffset, long[] destOrigin, long[] destRegion) { + if (destOrigin.length!=3 || destRegion.length!=3) { + throw new IllegalArgumentException("origin and region must both be arrays of length 3"); + } + Utils.pointerBuffers[0].rewind(); + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[2].rewind(); + Utils.pointerBuffers[1].put(destOrigin).position(0); + Utils.pointerBuffers[2].put(destRegion).position(0); + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10.clEnqueueCopyBufferToImage(q, buffer, ((LwjglImage) dest).getImage(), + srcOffset, Utils.pointerBuffers[1], Utils.pointerBuffers[2], null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueCopyBufferToImage"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(q.getCLEvent(event)); + } + + @Override + public Event acquireBufferForSharingAsync(CommandQueue queue) { + Utils.pointerBuffers[0].rewind(); + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10GL.clEnqueueAcquireGLObjects(q, buffer, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueAcquireGLObjects"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(q.getCLEvent(event)); + } + @Override + public void acquireBufferForSharingNoEvent(CommandQueue queue) { + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10GL.clEnqueueAcquireGLObjects(q, buffer, null, null); + Utils.checkError(ret, "clEnqueueAcquireGLObjects"); + } + + @Override + public Event releaseBufferForSharingAsync(CommandQueue queue) { + Utils.pointerBuffers[0].rewind(); + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10GL.clEnqueueReleaseGLObjects(q, buffer, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueReleaseGLObjects"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(q.getCLEvent(event)); + } + @Override + public void releaseBufferForSharingNoEvent(CommandQueue queue) { + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10GL.clEnqueueReleaseGLObjects(q, buffer, null, null); + Utils.checkError(ret, "clEnqueueReleaseGLObjects"); + } + + private static class ReleaserImpl implements ObjectReleaser { + private CLMem mem; + private ReleaserImpl(CLMem mem) { + this.mem = mem; + } + @Override + public void release() { + if (mem != null) { + int ret = CL10.clReleaseMemObject(mem); + mem = null; + Utils.reportError(ret, "clReleaseMemObject"); + } + } + + } +} diff --git a/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglCommandQueue.java b/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglCommandQueue.java new file mode 100644 index 000000000..c413d6a8e --- /dev/null +++ b/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglCommandQueue.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl.lwjgl; + +import com.jme3.opencl.CommandQueue; +import com.jme3.opencl.OpenCLObjectManager; +import org.lwjgl.opencl.CL10; +import org.lwjgl.opencl.CLCommandQueue; + +/** + * + * @author shaman + */ +public class LwjglCommandQueue extends CommandQueue { + + private final CLCommandQueue queue; + + public LwjglCommandQueue(CLCommandQueue queue) { + super(new ReleaserImpl(queue)); + this.queue = queue; + } + + public CLCommandQueue getQueue() { + return queue; + } + + @Override + public void flush() { + int ret = CL10.clFlush(queue); + Utils.checkError(ret, "clFlush"); + } + + @Override + public void finish() { + int ret = CL10.clFinish(queue); + Utils.checkError(ret, "clFinish"); + } + + private static class ReleaserImpl implements ObjectReleaser { + private CLCommandQueue queue; + private ReleaserImpl(CLCommandQueue queue) { + this.queue = queue; + } + @Override + public void release() { + if (queue != null) { + int ret = CL10.clReleaseCommandQueue(queue); + queue = null; + Utils.reportError(ret, "clReleaseCommandQueue"); + } + } + } +} diff --git a/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglContext.java b/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglContext.java new file mode 100644 index 000000000..c0b05c673 --- /dev/null +++ b/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglContext.java @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl.lwjgl; + +import com.jme3.opencl.*; +import com.jme3.opencl.Context; +import com.jme3.opencl.Image.ImageDescriptor; +import com.jme3.opencl.Image.ImageFormat; +import com.jme3.scene.VertexBuffer; +import com.jme3.texture.FrameBuffer; +import com.jme3.texture.Texture; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.lwjgl.BufferUtils; +import org.lwjgl.opencl.*; +import org.lwjgl.opengl.*; + +/** + * + * @author shaman + */ +public class LwjglContext extends Context { + private static final Logger LOG = Logger.getLogger(LwjglContext.class.getName()); + private final CLContext context; + private final List devices; + + public LwjglContext(CLContext context, List devices) { + super(new ReleaserImpl(context, devices)); + this.context = context; + this.devices = devices; + } + + public CLContext getContext() { + return context; + } + + @Override + public List 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 devices; + private ReleaserImpl(CLContext mem, List 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"); + } + } + + } +} diff --git a/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglDevice.java b/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglDevice.java new file mode 100644 index 000000000..7811e3a00 --- /dev/null +++ b/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglDevice.java @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl.lwjgl; + +import com.jme3.opencl.Device; +import com.jme3.opencl.Platform; +import java.util.Arrays; +import java.util.Collection; +import org.lwjgl.opencl.CL10; +import org.lwjgl.opencl.CL11; +import org.lwjgl.opencl.CLDevice; + +/** + * + * @author shaman + */ +public final class LwjglDevice implements Device { + + final CLDevice device; + final LwjglPlatform platform; + + public LwjglDevice(CLDevice device, LwjglPlatform platform) { + this.device = device; + this.platform = platform; + } + + public CLDevice getDevice() { + return device; + } + + @Override + public LwjglPlatform getPlatform() { + return platform; + } + + @Override + public DeviceType getDeviceType() { + int type = device.getInfoInt(CL10.CL_DEVICE_TYPE); + switch (type) { + case CL10.CL_DEVICE_TYPE_ACCELERATOR: return DeviceType.ACCELEARTOR; + case CL10.CL_DEVICE_TYPE_CPU: return DeviceType.CPU; + case CL10.CL_DEVICE_TYPE_GPU: return DeviceType.GPU; + default: return DeviceType.DEFAULT; + } + } + + @Override + public int getVendorId() { + return device.getInfoInt(CL10.CL_DEVICE_VENDOR_ID); + } + + @Override + public boolean isAvailable() { + return device.getInfoBoolean(CL10.CL_DEVICE_AVAILABLE); + } + + @Override + public boolean hasCompiler() { + return device.getInfoBoolean(CL10.CL_DEVICE_COMPILER_AVAILABLE); + } + + @Override + public boolean hasDouble() { + return hasExtension("cl_khr_fp64"); + } + + @Override + public boolean hasHalfFloat() { + return hasExtension("cl_khr_fp16"); + } + + @Override + public boolean hasErrorCorrectingMemory() { + return device.getInfoBoolean(CL10.CL_DEVICE_ERROR_CORRECTION_SUPPORT); + } + + @Override + public boolean hasUnifiedMemory() { + return device.getInfoBoolean(CL11.CL_DEVICE_HOST_UNIFIED_MEMORY); + } + + @Override + public boolean hasImageSupport() { + return device.getInfoBoolean(CL10.CL_DEVICE_IMAGE_SUPPORT); + } + + @Override + public boolean hasWritableImage3D() { + return hasExtension("cl_khr_3d_image_writes"); + } + + @Override + public boolean hasOpenGLInterop() { + return hasExtension("cl_khr_gl_sharing"); + } + + @Override + public boolean hasExtension(String extension) { + return getExtensions().contains(extension); + } + + @Override + public Collection 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(); + } + +} diff --git a/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglEvent.java b/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglEvent.java new file mode 100644 index 000000000..b7f992c95 --- /dev/null +++ b/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglEvent.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl.lwjgl; + +import com.jme3.opencl.Event; +import java.util.logging.Logger; +import org.lwjgl.opencl.CL10; +import org.lwjgl.opencl.CLEvent; + +/** + * + * @author shaman + */ +public class LwjglEvent extends Event { + private static final Logger LOG = Logger.getLogger(LwjglEvent.class.getName()); + private CLEvent event; + + public LwjglEvent(CLEvent event) { + super(new ReleaserImpl(event)); + this.event = event; + } + + public CLEvent getEvent() { + return event; + } + + @Override + public void waitForFinished() { + if (event==null) { + return; + } + CL10.clWaitForEvents(event); + release(); //short cut to save resources + } + + @Override + public boolean isCompleted() { + if (event==null) { + return true; + } + int status = event.getInfoInt(CL10.CL_EVENT_COMMAND_EXECUTION_STATUS); + if (status == CL10.CL_SUCCESS) { + release(); //short cut to save resources + return true; + } else if (status < 0) { + Utils.checkError(status, "EventStatus"); + return false; + } else { + return false; + } + } + + private static class ReleaserImpl implements ObjectReleaser { + private CLEvent event; + + private ReleaserImpl(CLEvent event) { + this.event = event; + } + + @Override + public void release() { + if (event != null && event.isValid()) { + int ret = CL10.clReleaseEvent(event); + event = null; + Utils.reportError(ret, "clReleaseEvent"); + LOG.finer("Event deleted"); + } + } + + } +} diff --git a/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglImage.java b/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglImage.java new file mode 100644 index 000000000..e41f69fe7 --- /dev/null +++ b/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglImage.java @@ -0,0 +1,575 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl.lwjgl; + +import com.jme3.math.ColorRGBA; +import com.jme3.opencl.*; +import java.nio.ByteBuffer; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.lwjgl.opencl.*; +import org.lwjgl.opencl.api.CLImageFormat; + +/** + * + * @author shaman + */ +public class LwjglImage extends Image { + private static final Logger LOG = Logger.getLogger(LwjglImage.class.getName()); + + private final CLMem image; + + public LwjglImage(CLMem image) { + super(new ReleaserImpl(image)); + this.image = image; + } + + public CLMem getImage() { + return image; + } + + public static int decodeImageChannelOrder(ImageChannelOrder order) { + switch (order) { + case A: + return CL10.CL_A; + case ARGB: + return CL10.CL_ARGB; + case BGRA: + return CL10.CL_BGRA; + case INTENSITY: + return CL10.CL_INTENSITY; + case LUMINANCE: + return CL10.CL_LUMINANCE; + case R: + return CL10.CL_R; + case RA: + return CL10.CL_RA; + case RG: + return CL10.CL_RG; + case RGB: + return CL10.CL_RGB; + case RGBA: + return CL10.CL_RGBA; + case RGBx: + return CL11.CL_RGBx; + case RGx: + return CL11.CL_RGx; + case Rx: + return CL11.CL_Rx; + default: + throw new IllegalArgumentException("unknown image channel order: " + order); + } + } + + public static ImageChannelOrder encodeImageChannelOrder(int order) { + switch (order) { + case CL10.CL_A: + return ImageChannelOrder.A; + case CL10.CL_ARGB: + return ImageChannelOrder.ARGB; + case CL10.CL_BGRA: + return ImageChannelOrder.BGRA; + case CL10.CL_INTENSITY: + return ImageChannelOrder.INTENSITY; + case CL10.CL_LUMINANCE: + return ImageChannelOrder.LUMINANCE; + case CL10.CL_R: + return ImageChannelOrder.R; + case CL10.CL_RA: + return ImageChannelOrder.RA; + case CL10.CL_RG: + return ImageChannelOrder.RG; + case CL10.CL_RGB: + return ImageChannelOrder.RGB; + case CL10.CL_RGBA: + return ImageChannelOrder.RGBA; + case CL11.CL_RGBx: + return ImageChannelOrder.RGBx; + case CL11.CL_RGx: + return ImageChannelOrder.RGx; + case CL11.CL_Rx: + return ImageChannelOrder.Rx; + default: + //throw new com.jme3.opencl.OpenCLException("unknown image channel order id: " + order); + LOG.log(Level.WARNING, "Unknown image channel order id: {0}", order); + return null; + } + } + + public static int decodeImageChannelType(ImageChannelType type) { + switch (type) { + case FLOAT: + return CL10.CL_FLOAT; + case HALF_FLOAT: + return CL10.CL_HALF_FLOAT; + case SIGNED_INT16: + return CL10.CL_SIGNED_INT16; + case SIGNED_INT32: + return CL10.CL_SIGNED_INT32; + case SIGNED_INT8: + return CL10.CL_SIGNED_INT8; + case SNORM_INT16: + return CL10.CL_SNORM_INT16; + case SNORM_INT8: + return CL10.CL_SNORM_INT8; + case UNORM_INT8: + return CL10.CL_UNORM_INT8; + case UNORM_INT_101010: + return CL10.CL_UNORM_INT_101010; + case UNORM_INT16: + return CL10.CL_UNORM_INT16; + case UNORM_SHORT_565: + return CL10.CL_UNORM_SHORT_565; + case UNORM_SHORT_555: + return CL10.CL_UNORM_SHORT_555; + case UNSIGNED_INT16: + return CL10.CL_UNSIGNED_INT16; + case UNSIGNED_INT32: + return CL10.CL_UNSIGNED_INT32; + case UNSIGNED_INT8: + return CL10.CL_UNSIGNED_INT8; + default: + throw new IllegalArgumentException("Unknown image channel type: " + type); + } + } + + public static ImageChannelType encodeImageChannelType(int type) { + switch (type) { + case CL10.CL_FLOAT: + return ImageChannelType.FLOAT; + case CL10.CL_HALF_FLOAT: + return ImageChannelType.HALF_FLOAT; + case CL10.CL_SIGNED_INT16: + return ImageChannelType.SIGNED_INT16; + case CL10.CL_SIGNED_INT32: + return ImageChannelType.SIGNED_INT32; + case CL10.CL_SIGNED_INT8: + return ImageChannelType.SIGNED_INT8; + case CL10.CL_SNORM_INT16: + return ImageChannelType.SNORM_INT16; + case CL10.CL_SNORM_INT8: + return ImageChannelType.SNORM_INT8; + case CL10.CL_UNORM_INT8: + return ImageChannelType.UNORM_INT8; + case CL10.CL_UNORM_INT16: + return ImageChannelType.UNORM_INT16; + case CL10.CL_UNORM_INT_101010: + return ImageChannelType.UNORM_INT_101010; + case CL10.CL_UNORM_SHORT_555: + return ImageChannelType.UNORM_SHORT_555; + case CL10.CL_UNORM_SHORT_565: + return ImageChannelType.UNORM_SHORT_565; + case CL10.CL_UNSIGNED_INT16: + return ImageChannelType.UNSIGNED_INT16; + case CL10.CL_UNSIGNED_INT32: + return ImageChannelType.UNSIGNED_INT32; + case CL10.CL_UNSIGNED_INT8: + return ImageChannelType.UNSIGNED_INT8; + default: + //throw new com.jme3.opencl.OpenCLException("unknown image channel type id: " + type); + LOG.log(Level.WARNING, "Unknown image channel type id: {0}", type); + return null; + } + } + + public static int decodeImageType(ImageType type) { + switch (type) { + case IMAGE_1D: + return CL12.CL_MEM_OBJECT_IMAGE1D; + case IMAGE_1D_ARRAY: + return CL12.CL_MEM_OBJECT_IMAGE1D_ARRAY; + case IMAGE_1D_BUFFER: + return CL12.CL_MEM_OBJECT_IMAGE1D_BUFFER; + case IMAGE_2D: + return CL10.CL_MEM_OBJECT_IMAGE2D; + case IMAGE_2D_ARRAY: + return CL12.CL_MEM_OBJECT_IMAGE2D_ARRAY; + case IMAGE_3D: + return CL10.CL_MEM_OBJECT_IMAGE3D; + default: + throw new IllegalArgumentException("Unknown image type: " + type); + } + } + + public static ImageType encodeImageType(int type) { + switch (type) { + case CL12.CL_MEM_OBJECT_IMAGE1D: + return ImageType.IMAGE_1D; + case CL12.CL_MEM_OBJECT_IMAGE1D_ARRAY: + return ImageType.IMAGE_1D_ARRAY; + case CL12.CL_MEM_OBJECT_IMAGE1D_BUFFER: + return ImageType.IMAGE_1D_BUFFER; + case CL10.CL_MEM_OBJECT_IMAGE2D: + return ImageType.IMAGE_2D; + case CL12.CL_MEM_OBJECT_IMAGE2D_ARRAY: + return ImageType.IMAGE_2D_ARRAY; + case CL10.CL_MEM_OBJECT_IMAGE3D: + return ImageType.IMAGE_3D; + default: + throw new com.jme3.opencl.OpenCLException("Unknown image type id: " + type); + } + } + + @Override + public long getWidth() { + return image.getImageInfoSize(CL10.CL_IMAGE_WIDTH); + } + + @Override + public long getHeight() { + return image.getImageInfoSize(CL10.CL_IMAGE_HEIGHT); + } + + @Override + public long getDepth() { + return image.getImageInfoSize(CL10.CL_IMAGE_DEPTH); + } + + @Override + public long getRowPitch() { + return image.getImageInfoSize(CL10.CL_IMAGE_ROW_PITCH); + } + + @Override + public long getSlicePitch() { + return image.getImageInfoSize(CL10.CL_IMAGE_SLICE_PITCH); + } + + @Override + public long getArraySize() { + return image.getImageInfoSize(CL12.CL_IMAGE_ARRAY_SIZE); + } + + @Override + public ImageFormat getImageFormat() { + CLImageFormat format = image.getImageFormat(); + return new ImageFormat( + encodeImageChannelOrder(format.getChannelOrder()), + encodeImageChannelType(format.getChannelType())); + } + + @Override + public ImageType getImageType() { + int type = image.getInfoInt(CL10.CL_MEM_TYPE); + return encodeImageType(type); + } + + @Override + public int getElementSize() { + return (int) image.getImageInfoSize(CL10.CL_IMAGE_ELEMENT_SIZE); + } + + @Override + public void readImage(CommandQueue queue, ByteBuffer dest, long[] origin, long[] region, long rowPitch, long slicePitch) { + if (origin.length!=3 || region.length!=3) { + throw new IllegalArgumentException("origin and region must both be arrays of length 3"); + } + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[2].rewind(); + Utils.pointerBuffers[1].put(origin).position(0); + Utils.pointerBuffers[2].put(region).position(0); + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10.clEnqueueReadImage(q, image, CL10.CL_TRUE, + Utils.pointerBuffers[1], Utils.pointerBuffers[2], + rowPitch, slicePitch, dest, null, null); + Utils.checkError(ret, "clEnqueueReadImage"); + } + + @Override + public Event readImageAsync(CommandQueue queue, ByteBuffer dest, long[] origin, long[] region, long rowPitch, long slicePitch) { + if (origin.length!=3 || region.length!=3) { + throw new IllegalArgumentException("origin and region must both be arrays of length 3"); + } + Utils.pointerBuffers[0].rewind(); + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[2].rewind(); + Utils.pointerBuffers[1].put(origin).position(0); + Utils.pointerBuffers[2].put(region).position(0); + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10.clEnqueueReadImage(q, image, CL10.CL_FALSE, + Utils.pointerBuffers[1], Utils.pointerBuffers[2], + rowPitch, slicePitch, dest, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueReadImage"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(q.getCLEvent(event)); + } + + @Override + public void writeImage(CommandQueue queue, ByteBuffer dest, long[] origin, long[] region, long rowPitch, long slicePitch) { + if (origin.length!=3 || region.length!=3) { + throw new IllegalArgumentException("origin and region must both be arrays of length 3"); + } + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[2].rewind(); + Utils.pointerBuffers[1].put(origin).position(0); + Utils.pointerBuffers[2].put(region).position(0); + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10.clEnqueueWriteImage(q, image, CL10.CL_TRUE, + Utils.pointerBuffers[1], Utils.pointerBuffers[2], + rowPitch, slicePitch, dest, null, null); + Utils.checkError(ret, "clEnqueueWriteImage"); + } + + @Override + public Event writeImageAsync(CommandQueue queue, ByteBuffer dest, long[] origin, long[] region, long rowPitch, long slicePitch) { + if (origin.length!=3 || region.length!=3) { + throw new IllegalArgumentException("origin and region must both be arrays of length 3"); + } + Utils.pointerBuffers[0].rewind(); + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[2].rewind(); + Utils.pointerBuffers[1].put(origin).position(0); + Utils.pointerBuffers[2].put(region).position(0); + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10.clEnqueueWriteImage(q, image, CL10.CL_FALSE, + Utils.pointerBuffers[1], Utils.pointerBuffers[2], + rowPitch, slicePitch, dest, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueWriteImage"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(q.getCLEvent(event)); + } + + @Override + public void copyTo(CommandQueue queue, Image dest, long[] srcOrigin, long[] destOrigin, long[] region) { + if (srcOrigin.length!=3 || destOrigin.length!=3 || region.length!=3) { + throw new IllegalArgumentException("origin and region must both be arrays of length 3"); + } + Utils.pointerBuffers[0].rewind(); + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[2].rewind(); + Utils.pointerBuffers[3].rewind(); + Utils.pointerBuffers[1].put(srcOrigin).position(0); + Utils.pointerBuffers[2].put(destOrigin).position(0); + Utils.pointerBuffers[3].put(region).position(0); + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10.clEnqueueCopyImage(q, image, ((LwjglImage) dest).getImage(), + Utils.pointerBuffers[1], Utils.pointerBuffers[2], Utils.pointerBuffers[3], + null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueCopyImage"); + long event = Utils.pointerBuffers[0].get(0); + ret = CL10.clWaitForEvents(q.getCLEvent(event)); + Utils.checkError(ret, "clWaitForEvents"); + } + + @Override + public Event copyToAsync(CommandQueue queue, Image dest, long[] srcOrigin, long[] destOrigin, long[] region) { + if (srcOrigin.length!=3 || destOrigin.length!=3 || region.length!=3) { + throw new IllegalArgumentException("origin and region must both be arrays of length 3"); + } + Utils.pointerBuffers[0].rewind(); + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[2].rewind(); + Utils.pointerBuffers[3].rewind(); + Utils.pointerBuffers[1].put(srcOrigin).position(0); + Utils.pointerBuffers[2].put(destOrigin).position(0); + Utils.pointerBuffers[3].put(region).position(0); + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10.clEnqueueCopyImage(q, image, ((LwjglImage) dest).getImage(), + Utils.pointerBuffers[1], Utils.pointerBuffers[2], Utils.pointerBuffers[3], + null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueCopyImage"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(q.getCLEvent(event)); + } + + @Override + public ImageMapping map(CommandQueue queue, long[] origin, long[] region, MappingAccess access) { + if (origin.length!=3 || region.length!=3) { + throw new IllegalArgumentException("origin and region must both be arrays of length 3"); + } + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[2].rewind(); + Utils.pointerBuffers[3].rewind(); + Utils.pointerBuffers[4].rewind(); + Utils.pointerBuffers[1].put(origin).position(0); + Utils.pointerBuffers[2].put(region).position(0); + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + long flags = Utils.getMappingAccessFlags(access); + Utils.errorBuffer.rewind(); + ByteBuffer buf = CL10.clEnqueueMapImage(q, image, CL10.CL_TRUE, flags, + Utils.pointerBuffers[1], Utils.pointerBuffers[2], + Utils.pointerBuffers[3], Utils.pointerBuffers[4], null, null, Utils.errorBuffer); + Utils.checkError(Utils.errorBuffer, "clEnqueueMapBuffer"); + return new ImageMapping(buf, Utils.pointerBuffers[3].get(0), Utils.pointerBuffers[4].get(0)); + } + + @Override + public ImageMapping mapAsync(CommandQueue queue, long[] origin, long[] region, MappingAccess access) { + if (origin.length!=3 || region.length!=3) { + throw new IllegalArgumentException("origin and region must both be arrays of length 3"); + } + Utils.pointerBuffers[0].rewind(); + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[2].rewind(); + Utils.pointerBuffers[3].rewind(); + Utils.pointerBuffers[4].rewind(); + Utils.pointerBuffers[1].put(origin).position(0); + Utils.pointerBuffers[2].put(region).position(0); + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + long flags = Utils.getMappingAccessFlags(access); + Utils.errorBuffer.rewind(); + ByteBuffer buf = CL10.clEnqueueMapImage(q, image, CL10.CL_FALSE, flags, + Utils.pointerBuffers[1], Utils.pointerBuffers[2], + Utils.pointerBuffers[3], Utils.pointerBuffers[4], null, Utils.pointerBuffers[0], Utils.errorBuffer); + Utils.checkError(Utils.errorBuffer, "clEnqueueMapBuffer"); + long event = Utils.pointerBuffers[0].get(0); + return new ImageMapping(buf, Utils.pointerBuffers[3].get(0), Utils.pointerBuffers[4].get(0), new LwjglEvent(q.getCLEvent(event))); + } + + @Override + public void unmap(CommandQueue queue, ImageMapping mapping) { + mapping.buffer.position(0); + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + Utils.pointerBuffers[0].rewind(); + int ret = CL10.clEnqueueUnmapMemObject(q, image, mapping.buffer, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueUnmapMemObject"); + long event = Utils.pointerBuffers[0].get(0); + ret = CL10.clWaitForEvents(q.getCLEvent(event)); + Utils.checkError(ret, "clWaitForEvents"); + } + + @Override + public Event fillAsync(CommandQueue queue, long[] origin, long[] region, ColorRGBA color) { + if (origin.length!=3 || region.length!=3) { + throw new IllegalArgumentException("origin and region must both be arrays of length 3"); + } + Utils.pointerBuffers[0].rewind(); + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[2].rewind(); + Utils.pointerBuffers[1].put(origin).position(0); + Utils.pointerBuffers[2].put(region).position(0); + Utils.tempBuffers[0].b16f.rewind(); + Utils.tempBuffers[0].b16f.limit(4); + Utils.tempBuffers[0].b16f.put(color.r).put(color.g).put(color.b).put(color.a); + Utils.tempBuffers[0].b16.rewind(); + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL12.clEnqueueFillImage(q, image, Utils.tempBuffers[0].b16, + Utils.pointerBuffers[1], Utils.pointerBuffers[2], null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueFillImage"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(q.getCLEvent(event)); + //TODO: why does q.getCLEvent(event) return null? + //This is a bug in LWJGL: they forgot to include the line + // if ( __result == CL_SUCCESS ) command_queue.registerCLEvent(event); + // after the native call + } + + @Override + public Event fillAsync(CommandQueue queue, long[] origin, long[] region, int[] color) { + if (color.length != 4) { + throw new IllegalArgumentException("the passed color array must have length 4"); + } + if (origin.length!=3 || region.length!=3) { + throw new IllegalArgumentException("origin and region must both be arrays of length 3"); + } + Utils.pointerBuffers[0].rewind(); + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[2].rewind(); + Utils.pointerBuffers[1].put(origin).position(0); + Utils.pointerBuffers[2].put(region).position(0); + Utils.tempBuffers[0].b16i.rewind(); + Utils.tempBuffers[0].b16i.limit(4); + Utils.tempBuffers[0].b16i.put(color); + Utils.tempBuffers[0].b16.rewind(); + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL12.clEnqueueFillImage(q, image, Utils.tempBuffers[0].b16, + Utils.pointerBuffers[1], Utils.pointerBuffers[2], null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueFillImage"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(q.getCLEvent(event)); + } + + @Override + public Event copyToBufferAsync(CommandQueue queue, Buffer dest, long[] srcOrigin, long[] srcRegion, long destOffset) { + if (srcOrigin.length!=3 || srcRegion.length!=3) { + throw new IllegalArgumentException("origin and region must both be arrays of length 3"); + } + Utils.pointerBuffers[0].rewind(); + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[2].rewind(); + Utils.pointerBuffers[1].put(srcOrigin).position(0); + Utils.pointerBuffers[2].put(srcRegion).position(0); + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10.clEnqueueCopyImageToBuffer(q, image, ((LwjglBuffer) dest).getBuffer(), + Utils.pointerBuffers[1], Utils.pointerBuffers[2], destOffset, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueCopyImageToBuffer"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(q.getCLEvent(event)); + } + + @Override + public Event acquireImageForSharingAsync(CommandQueue queue) { + Utils.pointerBuffers[0].rewind(); + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10GL.clEnqueueAcquireGLObjects(q, image, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueAcquireGLObjects"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(q.getCLEvent(event)); + } + @Override + public void acquireImageForSharingNoEvent(CommandQueue queue) { + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10GL.clEnqueueAcquireGLObjects(q, image, null, null); + Utils.checkError(ret, "clEnqueueAcquireGLObjects"); + } + @Override + public Event releaseImageForSharingAsync(CommandQueue queue) { + Utils.pointerBuffers[0].rewind(); + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10GL.clEnqueueReleaseGLObjects(q, image, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueReleaseGLObjects"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(q.getCLEvent(event)); + } + @Override + public void releaseImageForSharingNoEvent(CommandQueue queue) { + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10GL.clEnqueueReleaseGLObjects(q, image, null, null); + Utils.checkError(ret, "clEnqueueReleaseGLObjects"); + } + + private static class ReleaserImpl implements ObjectReleaser { + private CLMem mem; + private ReleaserImpl(CLMem mem) { + this.mem = mem; + } + @Override + public void release() { + if (mem != null) { + int ret = CL10.clReleaseMemObject(mem); + mem = null; + Utils.reportError(ret, "clReleaseMemObject"); + } + } + + } +} diff --git a/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglKernel.java b/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglKernel.java new file mode 100644 index 000000000..92ea60beb --- /dev/null +++ b/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglKernel.java @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl.lwjgl; + +import com.jme3.math.Matrix4f; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector2f; +import com.jme3.math.Vector4f; +import com.jme3.opencl.*; +import com.jme3.opencl.Buffer; +import java.nio.*; +import org.lwjgl.PointerBuffer; +import org.lwjgl.opencl.CL10; +import org.lwjgl.opencl.CLCommandQueue; +import org.lwjgl.opencl.CLDevice; +import org.lwjgl.opencl.CLKernel; + +/** + * + * @author shaman + */ +public class LwjglKernel extends Kernel { + + private final CLKernel kernel; + + public LwjglKernel(CLKernel kernel) { + super(new ReleaserImpl(kernel)); + this.kernel = kernel; + } + + public CLKernel getKernel() { + return kernel; + } + + @Override + public String getName() { + return kernel.getInfoString(CL10.CL_KERNEL_FUNCTION_NAME); + } + + @Override + public int getArgCount() { + return kernel.getInfoInt(CL10.CL_KERNEL_NUM_ARGS); + } + + @Override + public long getMaxWorkGroupSize(Device device) { + CLDevice d = ((LwjglDevice) device).getDevice(); + return kernel.getWorkGroupInfoSize(d, CL10.CL_KERNEL_WORK_GROUP_SIZE); + } + + @Override + public void setArg(int index, LocalMemPerElement t) { + int ret = CL10.clSetKernelArg (kernel, index, t.getSize() * workGroupSize.getSizes()[0] * workGroupSize.getSizes()[1] * workGroupSize.getSizes()[2]); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, LocalMem t) { + int ret = CL10.clSetKernelArg (kernel, index, t.getSize()); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, Buffer t) { + int ret = CL10.clSetKernelArg(kernel, index, ((LwjglBuffer) t).getBuffer()); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, Image i) { + int ret = CL10.clSetKernelArg(kernel, index, ((LwjglImage) i).getImage()); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, byte b) { + ByteBuffer buf = Utils.tempBuffers[0].b16; + buf.position(0); + buf.limit(1); + buf.put(0, b); + int ret = CL10.clSetKernelArg(kernel, index, buf); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, short s) { + ShortBuffer buf = Utils.tempBuffers[0].b16s; + buf.position(0); + buf.limit(1); + buf.put(0, s); + int ret = CL10.clSetKernelArg(kernel, index, buf); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, int i) { + IntBuffer buf = Utils.tempBuffers[0].b16i; + buf.position(0); + buf.limit(1); + buf.put(0, i); + int ret = CL10.clSetKernelArg(kernel, index, buf); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, long l) { + LongBuffer buf = Utils.tempBuffers[0].b16l; + buf.position(0); + buf.limit(1); + buf.put(0, l); + int ret = CL10.clSetKernelArg(kernel, index, buf); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, float f) { + FloatBuffer buf = Utils.tempBuffers[0].b16f; + buf.position(0); + buf.limit(1); + buf.put(0, f); + int ret = CL10.clSetKernelArg(kernel, index, buf); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, double d) { + DoubleBuffer buf = Utils.tempBuffers[0].b16d; + buf.position(0); + buf.limit(1); + buf.put(0, d); + int ret = CL10.clSetKernelArg(kernel, index, buf); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, Vector2f v) { + FloatBuffer buf = Utils.tempBuffers[0].b16f; + buf.position(0); + buf.limit(2); + buf.put(0, v.x); + buf.put(1, v.y); + int ret = CL10.clSetKernelArg(kernel, index, buf); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, Vector4f v) { + FloatBuffer buf = Utils.tempBuffers[0].b16f; + buf.position(0); + buf.limit(4); + buf.put(0, v.x); + buf.put(1, v.y); + buf.put(2, v.z); + buf.put(3, v.w); + int ret = CL10.clSetKernelArg(kernel, index, buf); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, Quaternion q) { + FloatBuffer buf = Utils.tempBuffers[0].b16f; + buf.position(0); + buf.limit(4); + buf.put(0, q.getX()); + buf.put(1, q.getY()); + buf.put(2, q.getZ()); + buf.put(3, q.getW()); + int ret = CL10.clSetKernelArg(kernel, index, buf); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, Matrix4f m) { + FloatBuffer buf = Utils.b80f; + buf.position(0); + buf.limit(16); + buf.put(m.m00).put(m.m01).put(m.m02).put(m.m03); + buf.put(m.m10).put(m.m11).put(m.m12).put(m.m13); + buf.put(m.m20).put(m.m21).put(m.m22).put(m.m23); + buf.put(m.m30).put(m.m31).put(m.m32).put(m.m33); + buf.position(0); + int ret = CL10.clSetKernelArg(kernel, index, buf); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, ByteBuffer buffer, long size) { + buffer.limit((int) (buffer.position() + size)); + int ret = CL10.clSetKernelArg(kernel, index, buffer); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public Event Run(CommandQueue queue) { + Utils.pointerBuffers[0].rewind(); + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[1].put(globalWorkSize.getSizes()); + Utils.pointerBuffers[1].position(0); + PointerBuffer p2 = null; + if (workGroupSize.getSizes()[0] > 0) { + p2 = Utils.pointerBuffers[2].rewind(); + p2.put(workGroupSize.getSizes()); + p2.position(0); + } + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10.clEnqueueNDRangeKernel(q, kernel, + globalWorkSize.getDimension(), null, Utils.pointerBuffers[1], + p2, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueNDRangeKernel"); + return new LwjglEvent(q.getCLEvent(Utils.pointerBuffers[0].get(0))); + } + @Override + public void RunNoEvent(CommandQueue queue) { + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[1].put(globalWorkSize.getSizes()); + Utils.pointerBuffers[1].position(0); + PointerBuffer p2 = null; + if (workGroupSize.getSizes()[0] > 0) { + p2 = Utils.pointerBuffers[2].rewind(); + p2.put(workGroupSize.getSizes()); + p2.position(0); + } + CLCommandQueue q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10.clEnqueueNDRangeKernel(q, kernel, + globalWorkSize.getDimension(), null, Utils.pointerBuffers[1], + p2, null, null); + Utils.checkError(ret, "clEnqueueNDRangeKernel"); + } + + @Override + public ObjectReleaser getReleaser() { + return new ReleaserImpl(kernel); + } + private static class ReleaserImpl implements ObjectReleaser { + private CLKernel kernel; + private ReleaserImpl(CLKernel kernel) { + this.kernel = kernel; + } + @Override + public void release() { + if (kernel != null) { + int ret = CL10.clReleaseKernel(kernel); + kernel = null; + Utils.reportError(ret, "clReleaseKernel"); + } + } + } +} diff --git a/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglPlatform.java b/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglPlatform.java new file mode 100644 index 000000000..7d22d0aca --- /dev/null +++ b/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglPlatform.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl.lwjgl; + +import com.jme3.opencl.Device; +import com.jme3.opencl.Platform; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import org.lwjgl.opencl.CL10; +import org.lwjgl.opencl.CLDevice; +import org.lwjgl.opencl.CLPlatform; + +/** + * + * @author shaman + */ +public final class LwjglPlatform implements Platform { + + final CLPlatform platform; + List devices; + + public LwjglPlatform(CLPlatform platform) { + this.platform = platform; + } + + public CLPlatform getPlatform() { + return platform; + } + + @Override + public List 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 getExtensions() { + return Arrays.asList(platform.getInfoString(CL10.CL_PLATFORM_EXTENSIONS).split(" ")); + } + +} diff --git a/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglProgram.java b/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglProgram.java new file mode 100644 index 000000000..f0a1b2df6 --- /dev/null +++ b/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglProgram.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl.lwjgl; + +import com.jme3.opencl.*; +import java.nio.ByteBuffer; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.lwjgl.PointerBuffer; +import org.lwjgl.opencl.*; + +/** + * + * @author shaman + */ +public class LwjglProgram extends Program { + private static final Logger LOG = Logger.getLogger(LwjglProgram.class.getName()); + + private final CLProgram program; + private final LwjglContext context; + + public LwjglProgram(CLProgram program, LwjglContext context) { + super(new ReleaserImpl(program)); + this.program = program; + this.context = context; + } + + public CLProgram getProgram() { + return program; + } + + @Override + public void build(String args, Device... devices) throws KernelCompilationException { + PointerBuffer deviceList = null; + if (devices != null) { + deviceList = PointerBuffer.allocateDirect(devices.length); + deviceList.rewind(); + for (Device d : devices) { + deviceList.put(((LwjglDevice) d).device.getPointer()); + } + deviceList.flip(); + } + int ret = CL10.clBuildProgram(program, deviceList, args, null); + if (ret != CL10.CL_SUCCESS) { + String log = Log(); + LOG.log(Level.WARNING, "Unable to compile program:\n{0}", log); + if (ret == CL10.CL_BUILD_PROGRAM_FAILURE) { + throw new KernelCompilationException("Failed to build program", ret, log); + } else { + Utils.checkError(ret, "clBuildProgram"); + } + } else { + LOG.log(Level.INFO, "Program compiled:\n{0}", Log()); + } + } + + private String Log() { + StringBuilder str = new StringBuilder(); + for (LwjglDevice device : context.getDevices()) { + CLDevice d = device.getDevice(); + str.append(device.getName()).append(":\n"); + str.append(program.getBuildInfoString(d, CL10.CL_PROGRAM_BUILD_LOG)); + str.append('\n'); + } + return str.toString(); + } + + @Override + public Kernel createKernel(String name) { + CLKernel kernel = CL10.clCreateKernel(program, name, Utils.errorBuffer); + Utils.checkError(Utils.errorBuffer, "clCreateKernel"); + return new LwjglKernel(kernel); + } + + @Override + public Kernel[] createAllKernels() { + CLKernel[] kernels = program.createKernelsInProgram(); + Kernel[] kx = new Kernel[kernels.length]; + for (int i=0; i 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); + } + } + +} diff --git a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java index be3014da5..68d46b1c7 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java +++ b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java @@ -35,6 +35,11 @@ package com.jme3.system.lwjgl; import com.jme3.input.lwjgl.JInputJoyInput; import com.jme3.input.lwjgl.LwjglKeyInput; import com.jme3.input.lwjgl.LwjglMouseInput; +import com.jme3.opencl.Device; +import com.jme3.opencl.PlatformChooser; +import com.jme3.opencl.lwjgl.LwjglDevice; +import com.jme3.opencl.lwjgl.LwjglPlatform; +import com.jme3.opencl.DefaultPlatformChooser; import com.jme3.renderer.Renderer; import com.jme3.renderer.RendererException; import com.jme3.renderer.lwjgl.LwjglGL; @@ -54,12 +59,15 @@ import com.jme3.renderer.opengl.GLTimingState; import com.jme3.renderer.opengl.GLTracer; import com.jme3.system.*; import java.io.File; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; import org.lwjgl.LWJGLException; import org.lwjgl.Sys; +import org.lwjgl.opencl.*; import org.lwjgl.opengl.*; /** @@ -81,6 +89,9 @@ public abstract class LwjglContext implements JmeContext { protected JInputJoyInput joyInput; protected Timer timer; protected SystemListener listener; + + protected LwjglPlatform clPlatform; + protected com.jme3.opencl.lwjgl.LwjglContext clContext; public void setSystemListener(SystemListener listener) { this.listener = listener; @@ -245,8 +256,108 @@ public abstract class LwjglContext implements JmeContext { if (joyInput != null) { joyInput.initialize(); } + } + @SuppressWarnings("unchecked") + protected void initOpenCL() { + logger.info("Initialize OpenCL wiht LWJGL2"); + + try { + CL.create(); + } catch (LWJGLException ex) { + logger.log(Level.SEVERE, "Unable to initialize OpenCL", ex); + return; + } + + //load platforms and devices + StringBuilder platformInfos = new StringBuilder(); + ArrayList platforms = new ArrayList<>(); + for (CLPlatform p : CLPlatform.getPlatforms()) { + platforms.add(new LwjglPlatform(p)); + } + platformInfos.append("Available OpenCL platforms:"); + for (int i=0; i devices = platform.getDevices(); + platformInfos.append("\n * Available devices:"); + for (int j=0; j choosenDevices = chooser.chooseDevices(platforms); + List 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) 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; + } } diff --git a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglDisplay.java b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglDisplay.java index 71ba74388..0fb787efe 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglDisplay.java +++ b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglDisplay.java @@ -149,6 +149,10 @@ public class LwjglDisplay extends LwjglAbstractDisplay { GL11.glEnable(ARBMultisample.GL_MULTISAMPLE_ARB); } } + + if (settings.isOpenCLSupport()) { + initOpenCL(); + } } protected void destroyContext(){ diff --git a/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglBuffer.java b/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglBuffer.java new file mode 100644 index 000000000..ea0eb9543 --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglBuffer.java @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl.lwjgl; + +import com.jme3.opencl.*; +import java.nio.ByteBuffer; +import org.lwjgl.opencl.*; + +/** + * + * @author shaman + */ +public class LwjglBuffer extends Buffer { + + private final long buffer; + + public LwjglBuffer(long buffer) { + super(new ReleaserImpl(buffer)); + this.buffer = buffer; + } + public long getBuffer() { + return buffer; + } + + @Override + public long getSize() { + return Info.clGetMemObjectInfoLong(buffer, CL10.CL_MEM_SIZE); + } + + @Override + public MemoryAccess getMemoryAccessFlags() { + return Utils.getMemoryAccessFromFlag(Info.clGetMemObjectInfoLong(buffer, CL10.CL_MEM_FLAGS)); + } + + @Override + public void read(CommandQueue queue, ByteBuffer dest, long size, long offset) { + //Note: LWJGL does not support the size parameter, I have to set the buffer limit + dest.limit((int) (dest.position() + size)); + int ret = CL10.clEnqueueReadBuffer(((LwjglCommandQueue)queue).getQueue(), + buffer, CL10.CL_TRUE, offset, dest, null, null); + Utils.checkError(ret, "clEnqueueReadBuffer"); + } + + @Override + public Event readAsync(CommandQueue queue, ByteBuffer dest, long size, long offset) { + //Note: LWJGL does not support the size parameter, I have to set the buffer limit + dest.limit((int) (dest.position() + size)); + Utils.pointerBuffers[0].rewind(); + long q = ((LwjglCommandQueue)queue).getQueue(); + int ret = CL10.clEnqueueReadBuffer(q, buffer, CL10.CL_FALSE, offset, dest, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueReadBuffer"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(event); + } + + @Override + public void write(CommandQueue queue, ByteBuffer src, long size, long offset) { + //Note: LWJGL does not support the size parameter, I have to set the buffer limit + src.limit((int) (src.position() + size)); + long q = ((LwjglCommandQueue)queue).getQueue(); + int ret = CL10.clEnqueueWriteBuffer(q, buffer, CL10.CL_TRUE, offset, src, null, null); + Utils.checkError(ret, "clEnqueueWriteBuffer"); + } + + @Override + public Event writeAsync(CommandQueue queue, ByteBuffer src, long size, long offset) { + //Note: LWJGL does not support the size parameter, I have to set the buffer limit + src.limit((int) (src.position() + size)); + Utils.pointerBuffers[0].rewind(); + long q = ((LwjglCommandQueue)queue).getQueue(); + int ret = CL10.clEnqueueWriteBuffer(q, buffer, CL10.CL_FALSE, offset, src, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueWriteBuffer"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(event); + } + + @Override + public void copyTo(CommandQueue queue, Buffer dest, long size, long srcOffset, long destOffset) { + long q = ((LwjglCommandQueue)queue).getQueue(); + Utils.pointerBuffers[0].rewind(); + int ret = CL10.clEnqueueCopyBuffer(q, buffer, ((LwjglBuffer) dest).buffer, srcOffset, destOffset, size, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueCopyBuffer"); + long event = Utils.pointerBuffers[0].get(0); + ret = CL10.clWaitForEvents(event); + Utils.checkError(ret, "clWaitForEvents"); + } + + @Override + public Event copyToAsync(CommandQueue queue, Buffer dest, long size, long srcOffset, long destOffset) { + long q = ((LwjglCommandQueue)queue).getQueue(); + Utils.pointerBuffers[0].rewind(); + int ret = CL10.clEnqueueCopyBuffer(q, buffer, ((LwjglBuffer) dest).buffer, srcOffset, destOffset, size, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueCopyBuffer"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(event); + } + + @Override + public ByteBuffer map(CommandQueue queue, long size, long offset, MappingAccess access) { + long q = ((LwjglCommandQueue) queue).getQueue(); + long flags = Utils.getMappingAccessFlags(access); + Utils.errorBuffer.rewind(); + ByteBuffer b = CL10.clEnqueueMapBuffer(q, buffer, CL10.CL_TRUE, flags, offset, size, null, null, Utils.errorBuffer, null); + Utils.checkError(Utils.errorBuffer, "clEnqueueMapBuffer"); + return b; + } + + @Override + public void unmap(CommandQueue queue, ByteBuffer ptr) { + ptr.position(0); + long q = ((LwjglCommandQueue) queue).getQueue(); + Utils.pointerBuffers[0].rewind(); + int ret = CL10.clEnqueueUnmapMemObject(q, buffer, ptr, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueUnmapMemObject"); + long event = Utils.pointerBuffers[0].get(0); + ret = CL10.clWaitForEvents(event); + Utils.checkError(ret, "clWaitForEvents"); + } + + @Override + public com.jme3.opencl.Buffer.AsyncMapping mapAsync(CommandQueue queue, long size, long offset, MappingAccess access) { + Utils.pointerBuffers[0].rewind(); + Utils.errorBuffer.rewind(); + long q = ((LwjglCommandQueue) queue).getQueue(); + long flags = Utils.getMappingAccessFlags(access); + ByteBuffer buf = CL10.clEnqueueMapBuffer(q, buffer, CL10.CL_FALSE, flags, offset, size, null, Utils.pointerBuffers[0], Utils.errorBuffer, null); + Utils.checkError(Utils.errorBuffer, "clEnqueueMapBuffer"); + long event = Utils.pointerBuffers[0].get(0); + return new com.jme3.opencl.Buffer.AsyncMapping(new LwjglEvent(event), buf); + } + + @Override + public Event fillAsync(CommandQueue queue, ByteBuffer pattern, long size, long offset) { + Utils.pointerBuffers[0].rewind(); + long q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL12.clEnqueueFillBuffer(q, buffer, pattern, offset, size, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueFillBuffer"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(event); + } + + @Override + public Event copyToImageAsync(CommandQueue queue, Image dest, long srcOffset, long[] destOrigin, long[] destRegion) { + if (destOrigin.length!=3 || destRegion.length!=3) { + throw new IllegalArgumentException("origin and region must both be arrays of length 3"); + } + Utils.pointerBuffers[0].rewind(); + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[2].rewind(); + Utils.pointerBuffers[1].put(destOrigin).position(0); + Utils.pointerBuffers[2].put(destRegion).position(0); + long q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10.clEnqueueCopyBufferToImage(q, buffer, ((LwjglImage) dest).getImage(), + srcOffset, Utils.pointerBuffers[1], Utils.pointerBuffers[2], null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueCopyBufferToImage"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(event); + } + + @Override + public Event acquireBufferForSharingAsync(CommandQueue queue) { + Utils.pointerBuffers[0].rewind(); + long q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10GL.clEnqueueAcquireGLObjects(q, buffer, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueAcquireGLObjects"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(event); + } + @Override + public void acquireBufferForSharingNoEvent(CommandQueue queue) { + long q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10GL.clEnqueueAcquireGLObjects(q, buffer, null, null); + Utils.checkError(ret, "clEnqueueAcquireGLObjects"); + } + + @Override + public Event releaseBufferForSharingAsync(CommandQueue queue) { + Utils.assertSharingPossible(); + Utils.pointerBuffers[0].rewind(); + long q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10GL.clEnqueueReleaseGLObjects(q, buffer, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueReleaseGLObjects"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(event); + } + @Override + public void releaseBufferForSharingNoEvent(CommandQueue queue) { + Utils.assertSharingPossible(); + long q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10GL.clEnqueueReleaseGLObjects(q, buffer, null, null); + Utils.checkError(ret, "clEnqueueReleaseGLObjects"); + } + + private static class ReleaserImpl implements ObjectReleaser { + private long mem; + private ReleaserImpl(long mem) { + this.mem = mem; + } + @Override + public void release() { + if (mem != 0) { + int ret = CL10.clReleaseMemObject(mem); + mem = 0; + Utils.reportError(ret, "clReleaseMemObject"); + } + } + + } +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglCommandQueue.java b/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglCommandQueue.java new file mode 100644 index 000000000..5be22813a --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglCommandQueue.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl.lwjgl; + +import com.jme3.opencl.CommandQueue; +import org.lwjgl.opencl.CL10; + +/** + * + * @author shaman + */ +public class LwjglCommandQueue extends CommandQueue { + + private final long queue; + + public LwjglCommandQueue(long queue) { + super(new ReleaserImpl(queue)); + this.queue = queue; + } + + public long getQueue() { + return queue; + } + + @Override + public void flush() { + int ret = CL10.clFlush(queue); + Utils.checkError(ret, "clFlush"); + } + + @Override + public void finish() { + int ret = CL10.clFinish(queue); + Utils.checkError(ret, "clFinish"); + } + + private static class ReleaserImpl implements ObjectReleaser { + private long queue; + private ReleaserImpl(long queue) { + this.queue = queue; + } + @Override + public void release() { + if (queue != 0) { + int ret = CL10.clReleaseCommandQueue(queue); + queue = 0; + Utils.reportError(ret, "clReleaseCommandQueue"); + } + } + } +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglContext.java b/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglContext.java new file mode 100644 index 000000000..83d43ca77 --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglContext.java @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl.lwjgl; + +import com.jme3.opencl.*; +import com.jme3.opencl.Context; +import com.jme3.opencl.Image.ImageDescriptor; +import com.jme3.opencl.Image.ImageFormat; +import com.jme3.scene.VertexBuffer; +import com.jme3.texture.FrameBuffer; +import com.jme3.texture.Texture; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.lwjgl.BufferUtils; +import org.lwjgl.opencl.*; +import org.lwjgl.opengl.*; + +/** + * + * @author shaman + */ +public class LwjglContext extends Context { + private static final Logger LOG = Logger.getLogger(LwjglContext.class.getName()); + private final long context; + private final List devices; + + public LwjglContext(long context, List devices) { + super(new ReleaserImpl(context, devices)); + this.context = context; + this.devices = devices; + } + + public long getContext() { + return context; + } + + @Override + public List 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 devices; + private ReleaserImpl(long mem, List 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"); + } + } + + } +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglDevice.java b/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglDevice.java new file mode 100644 index 000000000..31325a469 --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglDevice.java @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl.lwjgl; + +import com.jme3.opencl.Device; +import com.jme3.opencl.Platform; +import java.util.Arrays; +import java.util.Collection; +import org.lwjgl.PointerBuffer; +import org.lwjgl.opencl.CL10; +import org.lwjgl.opencl.CL11; +import org.lwjgl.opencl.CLDevice; +import org.lwjgl.opencl.Info; + +/** + * + * @author shaman + */ +public final class LwjglDevice implements Device { + + final CLDevice device; + final LwjglPlatform platform; + + public LwjglDevice(CLDevice device, LwjglPlatform platform) { + this.device = device; + this.platform = platform; + } + + public long getDevice() { + return device.address(); + } + public CLDevice getCLDevice() { + return device; + } + + @Override + public LwjglPlatform getPlatform() { + return platform; + } + + @Override + public DeviceType getDeviceType() { + int type = Info.clGetDeviceInfoInt(device.address(), CL10.CL_DEVICE_TYPE); + switch (type) { + case CL10.CL_DEVICE_TYPE_ACCELERATOR: return DeviceType.ACCELEARTOR; + case CL10.CL_DEVICE_TYPE_CPU: return DeviceType.CPU; + case CL10.CL_DEVICE_TYPE_GPU: return DeviceType.GPU; + default: return DeviceType.DEFAULT; + } + } + + @Override + public int getVendorId() { + return Info.clGetDeviceInfoInt(device.address(), CL10.CL_DEVICE_VENDOR_ID); + } + + @Override + public boolean isAvailable() { + return Info.clGetDeviceInfoBoolean(device.address(), CL10.CL_DEVICE_AVAILABLE); + } + + @Override + public boolean hasCompiler() { + return Info.clGetDeviceInfoBoolean(device.address(), CL10.CL_DEVICE_COMPILER_AVAILABLE); + } + + @Override + public boolean hasDouble() { + return hasExtension("cl_khr_fp64"); + } + + @Override + public boolean hasHalfFloat() { + return hasExtension("cl_khr_fp16"); + } + + @Override + public boolean hasErrorCorrectingMemory() { + return Info.clGetDeviceInfoBoolean(device.address(), CL10.CL_DEVICE_ERROR_CORRECTION_SUPPORT); + } + + @Override + public boolean hasUnifiedMemory() { + return Info.clGetDeviceInfoBoolean(device.address(), CL11.CL_DEVICE_HOST_UNIFIED_MEMORY); + } + + @Override + public boolean hasImageSupport() { + return Info.clGetDeviceInfoBoolean(device.address(), CL10.CL_DEVICE_IMAGE_SUPPORT); + } + + @Override + public boolean hasWritableImage3D() { + return hasExtension("cl_khr_3d_image_writes"); + } + + @Override + public boolean hasOpenGLInterop() { + return hasExtension("cl_khr_gl_sharing"); + } + + @Override + public boolean hasExtension(String extension) { + return getExtensions().contains(extension); + } + + @Override + public Collection 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(); + } + +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglEvent.java b/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglEvent.java new file mode 100644 index 000000000..5ded851d3 --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglEvent.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl.lwjgl; + +import com.jme3.opencl.Event; +import java.util.logging.Logger; +import org.lwjgl.opencl.CL10; +import org.lwjgl.opencl.Info; + +/** + * + * @author shaman + */ +public class LwjglEvent extends Event { + private static final Logger LOG = Logger.getLogger(LwjglEvent.class.getName()); + private long event; + + public LwjglEvent(long event) { + super(new ReleaserImpl(event)); + this.event = event; + } + + public long getEvent() { + return event; + } + + @Override + public void waitForFinished() { + CL10.clWaitForEvents(event); + release(); //short cut to save resources + } + + @Override + public boolean isCompleted() { + int status = Info.clGetEventInfoInt(event, CL10.CL_EVENT_COMMAND_EXECUTION_STATUS); + if (status == CL10.CL_SUCCESS) { + release(); //short cut to save resources + return true; + } else if (status < 0) { + Utils.checkError(status, "EventStatus"); + return false; + } else { + return false; + } + } + + private static class ReleaserImpl implements ObjectReleaser { + private long event; + + private ReleaserImpl(long event) { + this.event = event; + } + + @Override + public void release() { + if (event != 0) { + int ret = CL10.clReleaseEvent(event); + event = 0; + Utils.reportError(ret, "clReleaseEvent"); + LOG.finer("Event deleted"); + } + } + + } +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglImage.java b/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglImage.java new file mode 100644 index 000000000..ed9239677 --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglImage.java @@ -0,0 +1,579 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl.lwjgl; + +import com.jme3.math.ColorRGBA; +import com.jme3.opencl.*; +import java.nio.ByteBuffer; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.lwjgl.opencl.*; + +/** + * + * @author shaman + */ +public class LwjglImage extends Image { + private static final Logger LOG = Logger.getLogger(LwjglImage.class.getName()); + + private final long image; + + public LwjglImage(long image) { + super(new ReleaserImpl(image)); + this.image = image; + } + + public long getImage() { + return image; + } + + public static int decodeImageChannelOrder(ImageChannelOrder order) { + switch (order) { + case A: + return CL10.CL_A; + case ARGB: + return CL10.CL_ARGB; + case BGRA: + return CL10.CL_BGRA; + case INTENSITY: + return CL10.CL_INTENSITY; + case LUMINANCE: + return CL10.CL_LUMINANCE; + case R: + return CL10.CL_R; + case RA: + return CL10.CL_RA; + case RG: + return CL10.CL_RG; + case RGB: + return CL10.CL_RGB; + case RGBA: + return CL10.CL_RGBA; + case RGBx: + return CL11.CL_RGBx; + case RGx: + return CL11.CL_RGx; + case Rx: + return CL11.CL_Rx; + default: + throw new IllegalArgumentException("unknown image channel order: " + order); + } + } + + public static ImageChannelOrder encodeImageChannelOrder(int order) { + switch (order) { + case CL10.CL_A: + return ImageChannelOrder.A; + case CL10.CL_ARGB: + return ImageChannelOrder.ARGB; + case CL10.CL_BGRA: + return ImageChannelOrder.BGRA; + case CL10.CL_INTENSITY: + return ImageChannelOrder.INTENSITY; + case CL10.CL_LUMINANCE: + return ImageChannelOrder.LUMINANCE; + case CL10.CL_R: + return ImageChannelOrder.R; + case CL10.CL_RA: + return ImageChannelOrder.RA; + case CL10.CL_RG: + return ImageChannelOrder.RG; + case CL10.CL_RGB: + return ImageChannelOrder.RGB; + case CL10.CL_RGBA: + return ImageChannelOrder.RGBA; + case CL11.CL_RGBx: + return ImageChannelOrder.RGBx; + case CL11.CL_RGx: + return ImageChannelOrder.RGx; + case CL11.CL_Rx: + return ImageChannelOrder.Rx; + default: + //throw new com.jme3.opencl.OpenCLException("unknown image channel order id: " + order); + LOG.log(Level.WARNING, "Unknown image channel order id: {0}", order); + return null; + } + } + + public static int decodeImageChannelType(ImageChannelType type) { + switch (type) { + case FLOAT: + return CL10.CL_FLOAT; + case HALF_FLOAT: + return CL10.CL_HALF_FLOAT; + case SIGNED_INT16: + return CL10.CL_SIGNED_INT16; + case SIGNED_INT32: + return CL10.CL_SIGNED_INT32; + case SIGNED_INT8: + return CL10.CL_SIGNED_INT8; + case SNORM_INT16: + return CL10.CL_SNORM_INT16; + case SNORM_INT8: + return CL10.CL_SNORM_INT8; + case UNORM_INT8: + return CL10.CL_UNORM_INT8; + case UNORM_INT_101010: + return CL10.CL_UNORM_INT_101010; + case UNORM_INT16: + return CL10.CL_UNORM_INT16; + case UNORM_SHORT_565: + return CL10.CL_UNORM_SHORT_565; + case UNORM_SHORT_555: + return CL10.CL_UNORM_SHORT_555; + case UNSIGNED_INT16: + return CL10.CL_UNSIGNED_INT16; + case UNSIGNED_INT32: + return CL10.CL_UNSIGNED_INT32; + case UNSIGNED_INT8: + return CL10.CL_UNSIGNED_INT8; + default: + throw new IllegalArgumentException("Unknown image channel type: " + type); + } + } + + public static ImageChannelType encodeImageChannelType(int type) { + switch (type) { + case CL10.CL_FLOAT: + return ImageChannelType.FLOAT; + case CL10.CL_HALF_FLOAT: + return ImageChannelType.HALF_FLOAT; + case CL10.CL_SIGNED_INT16: + return ImageChannelType.SIGNED_INT16; + case CL10.CL_SIGNED_INT32: + return ImageChannelType.SIGNED_INT32; + case CL10.CL_SIGNED_INT8: + return ImageChannelType.SIGNED_INT8; + case CL10.CL_SNORM_INT16: + return ImageChannelType.SNORM_INT16; + case CL10.CL_SNORM_INT8: + return ImageChannelType.SNORM_INT8; + case CL10.CL_UNORM_INT8: + return ImageChannelType.UNORM_INT8; + case CL10.CL_UNORM_INT16: + return ImageChannelType.UNORM_INT16; + case CL10.CL_UNORM_INT_101010: + return ImageChannelType.UNORM_INT_101010; + case CL10.CL_UNORM_SHORT_555: + return ImageChannelType.UNORM_SHORT_555; + case CL10.CL_UNORM_SHORT_565: + return ImageChannelType.UNORM_SHORT_565; + case CL10.CL_UNSIGNED_INT16: + return ImageChannelType.UNSIGNED_INT16; + case CL10.CL_UNSIGNED_INT32: + return ImageChannelType.UNSIGNED_INT32; + case CL10.CL_UNSIGNED_INT8: + return ImageChannelType.UNSIGNED_INT8; + default: + //throw new com.jme3.opencl.OpenCLException("unknown image channel type id: " + type); + LOG.log(Level.WARNING, "Unknown image channel type id: {0}", type); + return null; + } + } + + public static int decodeImageType(ImageType type) { + switch (type) { + case IMAGE_1D: + return CL12.CL_MEM_OBJECT_IMAGE1D; + case IMAGE_1D_ARRAY: + return CL12.CL_MEM_OBJECT_IMAGE1D_ARRAY; + case IMAGE_1D_BUFFER: + return CL12.CL_MEM_OBJECT_IMAGE1D_BUFFER; + case IMAGE_2D: + return CL10.CL_MEM_OBJECT_IMAGE2D; + case IMAGE_2D_ARRAY: + return CL12.CL_MEM_OBJECT_IMAGE2D_ARRAY; + case IMAGE_3D: + return CL10.CL_MEM_OBJECT_IMAGE3D; + default: + throw new IllegalArgumentException("Unknown image type: " + type); + } + } + + public static ImageType encodeImageType(int type) { + switch (type) { + case CL12.CL_MEM_OBJECT_IMAGE1D: + return ImageType.IMAGE_1D; + case CL12.CL_MEM_OBJECT_IMAGE1D_ARRAY: + return ImageType.IMAGE_1D_ARRAY; + case CL12.CL_MEM_OBJECT_IMAGE1D_BUFFER: + return ImageType.IMAGE_1D_BUFFER; + case CL10.CL_MEM_OBJECT_IMAGE2D: + return ImageType.IMAGE_2D; + case CL12.CL_MEM_OBJECT_IMAGE2D_ARRAY: + return ImageType.IMAGE_2D_ARRAY; + case CL10.CL_MEM_OBJECT_IMAGE3D: + return ImageType.IMAGE_3D; + default: + throw new com.jme3.opencl.OpenCLException("Unknown image type id: " + type); + } + } + + @Override + public long getWidth() { + return Info.clGetImageInfoPointer(image, CL10.CL_IMAGE_WIDTH); + } + + @Override + public long getHeight() { + return Info.clGetImageInfoPointer(image, CL10.CL_IMAGE_HEIGHT); + } + + @Override + public long getDepth() { + return Info.clGetImageInfoPointer(image, CL10.CL_IMAGE_DEPTH); + } + + @Override + public long getRowPitch() { + return Info.clGetImageInfoPointer(image, CL10.CL_IMAGE_ROW_PITCH); + } + + @Override + public long getSlicePitch() { + return Info.clGetImageInfoPointer(image, CL10.CL_IMAGE_SLICE_PITCH); + } + + @Override + public long getArraySize() { + return Info.clGetImageInfoPointer(image, CL12.CL_IMAGE_ARRAY_SIZE); + } + + @Override + public ImageFormat getImageFormat() { + Utils.b80.rewind(); + CLImageFormat format = new CLImageFormat(Utils.b80); + int ret = CL10.clGetImageInfo(image, CL10.CL_IMAGE_FORMAT, format.sizeof(), Utils.b80, null); + Utils.checkError(ret, "clGetImageInfo"); + return new ImageFormat(encodeImageChannelOrder(format.image_channel_order()), encodeImageChannelType(format.image_channel_data_type())); + } + + @Override + public ImageType getImageType() { + int type = Info.clGetMemObjectInfoInt(image, CL10.CL_MEM_TYPE); + return encodeImageType(type); + } + + @Override + public int getElementSize() { + return Info.clGetImageInfoInt(image, CL10.CL_IMAGE_ELEMENT_SIZE); + } + + @Override + public void readImage(CommandQueue queue, ByteBuffer dest, long[] origin, long[] region, long rowPitch, long slicePitch) { + if (origin.length!=3 || region.length!=3) { + throw new IllegalArgumentException("origin and region must both be arrays of length 3"); + } + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[2].rewind(); + Utils.pointerBuffers[1].put(origin).position(0); + Utils.pointerBuffers[2].put(region).position(0); + long q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10.clEnqueueReadImage(q, image, CL10.CL_TRUE, + Utils.pointerBuffers[1], Utils.pointerBuffers[2], + rowPitch, slicePitch, dest, null, null); + Utils.checkError(ret, "clEnqueueReadImage"); + } + + @Override + public Event readImageAsync(CommandQueue queue, ByteBuffer dest, long[] origin, long[] region, long rowPitch, long slicePitch) { + if (origin.length!=3 || region.length!=3) { + throw new IllegalArgumentException("origin and region must both be arrays of length 3"); + } + Utils.pointerBuffers[0].rewind(); + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[2].rewind(); + Utils.pointerBuffers[1].put(origin).position(0); + Utils.pointerBuffers[2].put(region).position(0); + long q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10.clEnqueueReadImage(q, image, CL10.CL_FALSE, + Utils.pointerBuffers[1], Utils.pointerBuffers[2], + rowPitch, slicePitch, dest, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueReadImage"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(event); + } + + @Override + public void writeImage(CommandQueue queue, ByteBuffer dest, long[] origin, long[] region, long rowPitch, long slicePitch) { + if (origin.length!=3 || region.length!=3) { + throw new IllegalArgumentException("origin and region must both be arrays of length 3"); + } + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[2].rewind(); + Utils.pointerBuffers[1].put(origin).position(0); + Utils.pointerBuffers[2].put(region).position(0); + long q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10.clEnqueueWriteImage(q, image, CL10.CL_TRUE, + Utils.pointerBuffers[1], Utils.pointerBuffers[2], + rowPitch, slicePitch, dest, null, null); + Utils.checkError(ret, "clEnqueueWriteImage"); + } + + @Override + public Event writeImageAsync(CommandQueue queue, ByteBuffer dest, long[] origin, long[] region, long rowPitch, long slicePitch) { + if (origin.length!=3 || region.length!=3) { + throw new IllegalArgumentException("origin and region must both be arrays of length 3"); + } + Utils.pointerBuffers[0].rewind(); + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[2].rewind(); + Utils.pointerBuffers[1].put(origin).position(0); + Utils.pointerBuffers[2].put(region).position(0); + long q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10.clEnqueueWriteImage(q, image, CL10.CL_FALSE, + Utils.pointerBuffers[1], Utils.pointerBuffers[2], + rowPitch, slicePitch, dest, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueWriteImage"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(event); + } + + @Override + public void copyTo(CommandQueue queue, Image dest, long[] srcOrigin, long[] destOrigin, long[] region) { + if (srcOrigin.length!=3 || destOrigin.length!=3 || region.length!=3) { + throw new IllegalArgumentException("origin and region must both be arrays of length 3"); + } + Utils.pointerBuffers[0].rewind(); + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[2].rewind(); + Utils.pointerBuffers[3].rewind(); + Utils.pointerBuffers[1].put(srcOrigin).position(0); + Utils.pointerBuffers[2].put(destOrigin).position(0); + Utils.pointerBuffers[3].put(region).position(0); + long q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10.clEnqueueCopyImage(q, image, ((LwjglImage) dest).getImage(), + Utils.pointerBuffers[1], Utils.pointerBuffers[2], Utils.pointerBuffers[3], + null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueCopyImage"); + long event = Utils.pointerBuffers[0].get(0); + ret = CL10.clWaitForEvents(event); + Utils.checkError(ret, "clWaitForEvents"); + } + + @Override + public Event copyToAsync(CommandQueue queue, Image dest, long[] srcOrigin, long[] destOrigin, long[] region) { + if (srcOrigin.length!=3 || destOrigin.length!=3 || region.length!=3) { + throw new IllegalArgumentException("origin and region must both be arrays of length 3"); + } + Utils.pointerBuffers[0].rewind(); + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[2].rewind(); + Utils.pointerBuffers[3].rewind(); + Utils.pointerBuffers[1].put(srcOrigin).position(0); + Utils.pointerBuffers[2].put(destOrigin).position(0); + Utils.pointerBuffers[3].put(region).position(0); + long q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10.clEnqueueCopyImage(q, image, ((LwjglImage) dest).getImage(), + Utils.pointerBuffers[1], Utils.pointerBuffers[2], Utils.pointerBuffers[3], + null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueCopyImage"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(event); + } + + @Override + public ImageMapping map(CommandQueue queue, long[] origin, long[] region, MappingAccess access) { + if (origin.length!=3 || region.length!=3) { + throw new IllegalArgumentException("origin and region must both be arrays of length 3"); + } + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[2].rewind(); + Utils.pointerBuffers[3].rewind(); + Utils.pointerBuffers[4].rewind(); + Utils.pointerBuffers[1].put(origin).position(0); + Utils.pointerBuffers[2].put(region).position(0); + long q = ((LwjglCommandQueue) queue).getQueue(); + long flags = Utils.getMappingAccessFlags(access); + Utils.errorBuffer.rewind(); + ByteBuffer buf = CL10.clEnqueueMapImage(q, image, CL10.CL_TRUE, flags, + Utils.pointerBuffers[1], Utils.pointerBuffers[2], + Utils.pointerBuffers[3], Utils.pointerBuffers[4], null, null, + Utils.errorBuffer, null); + Utils.checkError(Utils.errorBuffer, "clEnqueueMapBuffer"); + return new ImageMapping(buf, Utils.pointerBuffers[3].get(0), Utils.pointerBuffers[4].get(0)); + } + + @Override + public ImageMapping mapAsync(CommandQueue queue, long[] origin, long[] region, MappingAccess access) { + if (origin.length!=3 || region.length!=3) { + throw new IllegalArgumentException("origin and region must both be arrays of length 3"); + } + Utils.pointerBuffers[0].rewind(); + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[2].rewind(); + Utils.pointerBuffers[3].rewind(); + Utils.pointerBuffers[4].rewind(); + Utils.pointerBuffers[1].put(origin).position(0); + Utils.pointerBuffers[2].put(region).position(0); + long q = ((LwjglCommandQueue) queue).getQueue(); + long flags = Utils.getMappingAccessFlags(access); + Utils.errorBuffer.rewind(); + ByteBuffer buf = CL10.clEnqueueMapImage(q, image, CL10.CL_FALSE, flags, + Utils.pointerBuffers[1], Utils.pointerBuffers[2], + Utils.pointerBuffers[3], Utils.pointerBuffers[4], null, Utils.pointerBuffers[0], + Utils.errorBuffer, null); + Utils.checkError(Utils.errorBuffer, "clEnqueueMapBuffer"); + long event = Utils.pointerBuffers[0].get(0); + return new ImageMapping(buf, Utils.pointerBuffers[3].get(0), Utils.pointerBuffers[4].get(0), new LwjglEvent(event)); + } + + @Override + public void unmap(CommandQueue queue, ImageMapping mapping) { + mapping.buffer.position(0); + long q = ((LwjglCommandQueue) queue).getQueue(); + Utils.pointerBuffers[0].rewind(); + int ret = CL10.clEnqueueUnmapMemObject(q, image, mapping.buffer, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueUnmapMemObject"); + long event = Utils.pointerBuffers[0].get(0); + ret = CL10.clWaitForEvents(event); + Utils.checkError(ret, "clWaitForEvents"); + } + + @Override + public Event fillAsync(CommandQueue queue, long[] origin, long[] region, ColorRGBA color) { + if (origin.length!=3 || region.length!=3) { + throw new IllegalArgumentException("origin and region must both be arrays of length 3"); + } + Utils.pointerBuffers[0].rewind(); + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[2].rewind(); + Utils.pointerBuffers[1].put(origin).position(0); + Utils.pointerBuffers[2].put(region).position(0); + Utils.tempBuffers[0].b16f.rewind(); + Utils.tempBuffers[0].b16f.limit(4); + Utils.tempBuffers[0].b16f.put(color.r).put(color.g).put(color.b).put(color.a); + Utils.tempBuffers[0].b16.rewind(); + long q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL12.clEnqueueFillImage(q, image, Utils.tempBuffers[0].b16, + Utils.pointerBuffers[1], Utils.pointerBuffers[2], null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueFillImage"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(event); + //TODO: why does q.getCLEvent(event) return null? + //This is a bug in LWJGL: they forgot to include the line + // if ( __result == CL_SUCCESS ) command_queue.registerCLEvent(event); + // after the native call + } + + @Override + public Event fillAsync(CommandQueue queue, long[] origin, long[] region, int[] color) { + if (color.length != 4) { + throw new IllegalArgumentException("the passed color array must have length 4"); + } + if (origin.length!=3 || region.length!=3) { + throw new IllegalArgumentException("origin and region must both be arrays of length 3"); + } + Utils.pointerBuffers[0].rewind(); + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[2].rewind(); + Utils.pointerBuffers[1].put(origin).position(0); + Utils.pointerBuffers[2].put(region).position(0); + Utils.tempBuffers[0].b16i.rewind(); + Utils.tempBuffers[0].b16i.limit(4); + Utils.tempBuffers[0].b16i.put(color); + Utils.tempBuffers[0].b16.rewind(); + long q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL12.clEnqueueFillImage(q, image, Utils.tempBuffers[0].b16, + Utils.pointerBuffers[1], Utils.pointerBuffers[2], null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueFillImage"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(event); + } + + @Override + public Event copyToBufferAsync(CommandQueue queue, Buffer dest, long[] srcOrigin, long[] srcRegion, long destOffset) { + if (srcOrigin.length!=3 || srcRegion.length!=3) { + throw new IllegalArgumentException("origin and region must both be arrays of length 3"); + } + Utils.pointerBuffers[0].rewind(); + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[2].rewind(); + Utils.pointerBuffers[1].put(srcOrigin).position(0); + Utils.pointerBuffers[2].put(srcRegion).position(0); + long q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10.clEnqueueCopyImageToBuffer(q, image, ((LwjglBuffer) dest).getBuffer(), + Utils.pointerBuffers[1], Utils.pointerBuffers[2], destOffset, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueCopyImageToBuffer"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(event); + } + + @Override + public Event acquireImageForSharingAsync(CommandQueue queue) { + Utils.pointerBuffers[0].rewind(); + long q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10GL.clEnqueueAcquireGLObjects(q, image, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueAcquireGLObjects"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(event); + } + @Override + public void acquireImageForSharingNoEvent(CommandQueue queue) { + long q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10GL.clEnqueueAcquireGLObjects(q, image, null, null); + Utils.checkError(ret, "clEnqueueAcquireGLObjects"); + } + @Override + public Event releaseImageForSharingAsync(CommandQueue queue) { + Utils.assertSharingPossible(); + Utils.pointerBuffers[0].rewind(); + long q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10GL.clEnqueueReleaseGLObjects(q, image, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueReleaseGLObjects"); + long event = Utils.pointerBuffers[0].get(0); + return new LwjglEvent(event); + } + @Override + public void releaseImageForSharingNoEvent(CommandQueue queue) { + Utils.assertSharingPossible(); + long q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10GL.clEnqueueReleaseGLObjects(q, image, null, null); + Utils.checkError(ret, "clEnqueueReleaseGLObjects"); + } + + private static class ReleaserImpl implements ObjectReleaser { + private long mem; + private ReleaserImpl(long mem) { + this.mem = mem; + } + @Override + public void release() { + if (mem != 0) { + int ret = CL10.clReleaseMemObject(mem); + mem = 0; + Utils.reportError(ret, "clReleaseMemObject"); + } + } + + } +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglKernel.java b/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglKernel.java new file mode 100644 index 000000000..b3a668611 --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglKernel.java @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl.lwjgl; + +import com.jme3.math.Matrix4f; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector2f; +import com.jme3.math.Vector4f; +import com.jme3.opencl.*; +import com.jme3.opencl.Buffer; +import java.nio.*; +import org.lwjgl.PointerBuffer; +import org.lwjgl.opencl.CL10; +import org.lwjgl.opencl.CLDevice; +import org.lwjgl.opencl.Info; + +/** + * + * @author shaman + */ +public class LwjglKernel extends Kernel { + + private final long kernel; + + public LwjglKernel(long kernel) { + super(new ReleaserImpl(kernel)); + this.kernel = kernel; + } + + public long getKernel() { + return kernel; + } + + @Override + public String getName() { + return Info.clGetKernelInfoStringASCII(kernel, CL10.CL_KERNEL_FUNCTION_NAME); + } + + @Override + public int getArgCount() { + return Info.clGetKernelInfoInt(kernel, CL10.CL_KERNEL_NUM_ARGS); + } + + @Override + public long getMaxWorkGroupSize(Device device) { + long d = ((LwjglDevice) device).getDevice(); + return Info.clGetKernelWorkGroupInfoPointer(kernel, d, CL10.CL_KERNEL_WORK_GROUP_SIZE); + } + + @Override + public void setArg(int index, LocalMemPerElement t) { + int ret = CL10.clSetKernelArg (kernel, index, t.getSize() * workGroupSize.getSizes()[0] * workGroupSize.getSizes()[1] * workGroupSize.getSizes()[2]); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, LocalMem t) { + int ret = CL10.clSetKernelArg (kernel, index, t.getSize()); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, Buffer t) { + int ret = CL10.clSetKernelArg1p(kernel, index, ((LwjglBuffer) t).getBuffer()); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, Image i) { + int ret = CL10.clSetKernelArg1p(kernel, index, ((LwjglImage) i).getImage()); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, byte b) { + int ret = CL10.clSetKernelArg1b(kernel, index, b); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, short s) { + int ret = CL10.clSetKernelArg1s(kernel, index, s); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, int i) { + int ret = CL10.clSetKernelArg1i(kernel, index, i); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, long l) { + int ret = CL10.clSetKernelArg1l(kernel, index, l); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, float f) { + int ret = CL10.clSetKernelArg1f(kernel, index, f); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, double d) { + int ret = CL10.clSetKernelArg1d(kernel, index, d); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, Vector2f v) { + int ret = CL10.clSetKernelArg2f(kernel, index, v.x, v.y); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, Vector4f v) { + int ret = CL10.clSetKernelArg4f(kernel, index, v.x, v.y, v.z, v.w); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, Quaternion q) { + int ret = CL10.clSetKernelArg4f(kernel, index, q.getX(), q.getY(), q.getZ(), q.getW()); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, Matrix4f m) { + FloatBuffer buf = Utils.b80f; + buf.position(0); + buf.limit(16); + buf.put(m.m00).put(m.m01).put(m.m02).put(m.m03); + buf.put(m.m10).put(m.m11).put(m.m12).put(m.m13); + buf.put(m.m20).put(m.m21).put(m.m22).put(m.m23); + buf.put(m.m30).put(m.m31).put(m.m32).put(m.m33); + buf.position(0); + int ret = CL10.clSetKernelArg(kernel, index, buf); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public void setArg(int index, ByteBuffer buffer, long size) { + buffer.limit((int) (buffer.position() + size)); + int ret = CL10.clSetKernelArg(kernel, index, buffer); + Utils.checkError(ret, "clSetKernelArg"); + } + + @Override + public Event Run(CommandQueue queue) { + Utils.pointerBuffers[0].rewind(); + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[1].put(globalWorkSize.getSizes()); + Utils.pointerBuffers[1].position(0); + PointerBuffer p2 = null; + if (workGroupSize.getSizes()[0] > 0) { + p2 = Utils.pointerBuffers[2].rewind(); + p2.put(workGroupSize.getSizes()); + p2.position(0); + } + long q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10.clEnqueueNDRangeKernel(q, kernel, + globalWorkSize.getDimension(), null, Utils.pointerBuffers[1], + p2, null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clEnqueueNDRangeKernel"); + return new LwjglEvent(Utils.pointerBuffers[0].get(0)); + } + @Override + public void RunNoEvent(CommandQueue queue) { + Utils.pointerBuffers[1].rewind(); + Utils.pointerBuffers[1].put(globalWorkSize.getSizes()); + Utils.pointerBuffers[1].position(0); + PointerBuffer p2 = null; + if (workGroupSize.getSizes()[0] > 0) { + p2 = Utils.pointerBuffers[2].rewind(); + p2.put(workGroupSize.getSizes()); + p2.position(0); + } + long q = ((LwjglCommandQueue) queue).getQueue(); + int ret = CL10.clEnqueueNDRangeKernel(q, kernel, + globalWorkSize.getDimension(), null, Utils.pointerBuffers[1], + p2, null, null); + Utils.checkError(ret, "clEnqueueNDRangeKernel"); + } + + @Override + public ObjectReleaser getReleaser() { + return new ReleaserImpl(kernel); + } + private static class ReleaserImpl implements ObjectReleaser { + private long kernel; + private ReleaserImpl(long kernel) { + this.kernel = kernel; + } + @Override + public void release() { + if (kernel != 0) { + int ret = CL10.clReleaseKernel(kernel); + kernel = 0; + Utils.reportError(ret, "clReleaseKernel"); + } + } + } +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglPlatform.java b/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglPlatform.java new file mode 100644 index 000000000..74fc685c1 --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglPlatform.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl.lwjgl; + +import com.jme3.opencl.Device; +import com.jme3.opencl.Platform; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import org.lwjgl.opencl.CL10; +import org.lwjgl.opencl.CLDevice; +import org.lwjgl.opencl.CLPlatform; +import org.lwjgl.opencl.Info; + +/** + * + * @author shaman + */ +public final class LwjglPlatform implements Platform { + + final CLPlatform platform; + List devices; + + public LwjglPlatform(CLPlatform platform) { + this.platform = platform; + } + + public CLPlatform getPlatform() { + return platform; + } + + @Override + public List 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 getExtensions() { + return Arrays.asList(Info.clGetPlatformInfoStringASCII(platform.address(), CL10.CL_PLATFORM_EXTENSIONS).split(" ")); + } + +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglProgram.java b/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglProgram.java new file mode 100644 index 000000000..eb8faa199 --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglProgram.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2009-2016 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.opencl.lwjgl; + +import com.jme3.opencl.*; +import java.nio.ByteBuffer; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.lwjgl.BufferUtils; +import org.lwjgl.PointerBuffer; +import org.lwjgl.opencl.*; +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.system.Pointer; + +/** + * + * @author shaman + */ +public class LwjglProgram extends Program { + private static final Logger LOG = Logger.getLogger(LwjglProgram.class.getName()); + + private final long program; + private final LwjglContext context; + + public LwjglProgram(long program, LwjglContext context) { + super(new ReleaserImpl(program)); + this.program = program; + this.context = context; + } + + public long getProgram() { + return program; + } + + @Override + public void build(String args, Device... devices) throws KernelCompilationException { + PointerBuffer deviceList = null; + if (devices != null) { + deviceList = PointerBuffer.allocateDirect(devices.length); + deviceList.rewind(); + for (Device d : devices) { + deviceList.put(((LwjglDevice) d).getDevice()); + } + deviceList.flip(); + } + int ret = CL10.clBuildProgram(program, deviceList, args, null, 0); + if (ret != CL10.CL_SUCCESS) { + String log = Log(); + LOG.log(Level.WARNING, "Unable to compile program:\n{0}", log); + if (ret == CL10.CL_BUILD_PROGRAM_FAILURE) { + throw new KernelCompilationException("Failed to build program", ret, log); + } else { + Utils.checkError(ret, "clBuildProgram"); + } + } else { + LOG.log(Level.INFO, "Program compiled:\n{0}", Log()); + } + } + + private String Log(long device) { + Utils.pointerBuffers[0].rewind(); + int ret = CL10.clGetProgramBuildInfo(program, device, CL10.CL_PROGRAM_BUILD_LOG, (ByteBuffer) null, Utils.pointerBuffers[0]); + Utils.checkError(ret, "clGetProgramBuildInfo"); + int count = (int) Utils.pointerBuffers[0].get(0); + final ByteBuffer buffer = BufferUtils.createByteBuffer(count); + ret = CL10.clGetProgramBuildInfo(program, device, CL10.CL_PROGRAM_BUILD_LOG, buffer, null); + Utils.checkError(ret, "clGetProgramBuildInfo"); + return MemoryUtil.memDecodeASCII(buffer); + } + + private String Log() { + StringBuilder str = new StringBuilder(); + for (LwjglDevice device : context.getDevices()) { + long d = device.getDevice(); + str.append(device.getName()).append(":\n"); + //str.append(Info.clGetProgramBuildInfoStringASCII(program, d, CL10.CL_PROGRAM_BUILD_LOG)); //This throws an IllegalArgumentException in Buffer.limit() + str.append(Log(d)); + str.append('\n'); + } + return str.toString(); + } + + @Override + public Kernel createKernel(String name) { + long kernel = CL10.clCreateKernel(program, name, Utils.errorBuffer); + Utils.checkError(Utils.errorBuffer, "clCreateKernel"); + return new LwjglKernel(kernel); + } + + @Override + public Kernel[] createAllKernels() { + Utils.tempBuffers[0].b16i.rewind(); + int ret = CL10.clCreateKernelsInProgram(program, null, Utils.tempBuffers[0].b16i); + Utils.checkError(ret, "clCreateKernelsInProgram"); + int count = Utils.tempBuffers[0].b16i.get(0); + PointerBuffer buf = PointerBuffer.allocateDirect(count); + ret = CL10.clCreateKernelsInProgram(program, buf, null); + Utils.checkError(ret, "clCreateKernelsInProgram"); + Kernel[] kx = new Kernel[count]; + for (int i=0; i 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); + } + } + +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java index 20532df36..9c31f2085 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java @@ -35,6 +35,13 @@ package com.jme3.system.lwjgl; import com.jme3.input.lwjgl.GlfwJoystickInput; import com.jme3.input.lwjgl.GlfwKeyInput; import com.jme3.input.lwjgl.GlfwMouseInput; +import com.jme3.opencl.Context; +import com.jme3.opencl.DefaultPlatformChooser; +import com.jme3.opencl.Device; +import com.jme3.opencl.PlatformChooser; +import com.jme3.opencl.lwjgl.LwjglDevice; +import com.jme3.opencl.lwjgl.LwjglPlatform; +import com.jme3.opencl.lwjgl.Utils; import com.jme3.renderer.Renderer; import com.jme3.renderer.RendererException; import com.jme3.renderer.lwjgl.LwjglGL; @@ -43,17 +50,21 @@ import com.jme3.renderer.lwjgl.LwjglGLFboEXT; import com.jme3.renderer.lwjgl.LwjglGLFboGL3; import com.jme3.renderer.opengl.*; import com.jme3.system.*; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.lwjgl.PointerBuffer; import org.lwjgl.glfw.GLFW; +import org.lwjgl.opencl.*; +import org.lwjgl.opengl.ARBDebugOutput; import org.lwjgl.opengl.ARBFramebufferObject; import org.lwjgl.opengl.EXTFramebufferMultisample; import org.lwjgl.opengl.GLCapabilities; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.logging.Level; -import java.util.logging.Logger; import static org.lwjgl.glfw.GLFW.GLFW_TRUE; -import org.lwjgl.opengl.ARBDebugOutput; - +import static org.lwjgl.opencl.CL10.CL_CONTEXT_PLATFORM; import static org.lwjgl.opengl.GL.createCapabilities; import static org.lwjgl.opengl.GL11.glGetInteger; @@ -64,6 +75,8 @@ public abstract class LwjglContext implements JmeContext { private static final Logger logger = Logger.getLogger(LwjglContext.class.getName()); + public static final boolean CL_GL_SHARING_POSSIBLE = true; + protected static final String THREAD_NAME = "jME3 Main"; protected AtomicBoolean created = new AtomicBoolean(false); @@ -77,6 +90,8 @@ public abstract class LwjglContext implements JmeContext { protected GlfwJoystickInput joyInput; protected Timer timer; protected SystemListener listener; + + protected com.jme3.opencl.lwjgl.LwjglContext clContext; public void setSystemListener(SystemListener listener) { this.listener = listener; @@ -180,6 +195,149 @@ public abstract class LwjglContext implements JmeContext { joyInput.initialize(); } renderable.set(true); + + } + + protected void initOpenCL(long window) { + logger.info("Initialize OpenCL with LWJGL3"); + +// try { +// CL.create(); +// } catch (Exception ex) { +// logger.log(Level.SEVERE, "Unable to initialize OpenCL", ex); +// return; +// } + + //load platforms and devices + StringBuilder platformInfos = new StringBuilder(); + ArrayList platforms = new ArrayList<>(); + for (CLPlatform p : CLPlatform.getPlatforms()) { + platforms.add(new LwjglPlatform(p)); + } + platformInfos.append("Available OpenCL platforms:"); + for (int i=0; i devices = platform.getDevices(); + platformInfos.append("\n * Available devices:"); + for (int j=0; j choosenDevices = chooser.chooseDevices(platforms); + List 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) 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 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; + } + } diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java index cac8ecdd1..6e351d8b2 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java @@ -326,6 +326,13 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable { created.set(true); super.internalCreate(); + + //create OpenCL + //Must be done here because the window handle is needed + if (settings.isOpenCLSupport()) { + initOpenCL(window); + } + } catch (Exception ex) { try { if (window != NULL) {