diff --git a/jme3-core/src/main/java/com/jme3/opencl/Context.java b/jme3-core/src/main/java/com/jme3/opencl/Context.java index 0201f696a..8877b5823 100644 --- a/jme3-core/src/main/java/com/jme3/opencl/Context.java +++ b/jme3-core/src/main/java/com/jme3/opencl/Context.java @@ -51,8 +51,8 @@ public interface Context { Buffer createBuffer(int size, MemoryAccess access); Buffer createBuffer(int size); - Buffer useHostBuffer(ByteBuffer data, int size, MemoryAccess access); - Buffer useHostBuffer(ByteBuffer data, int size); + Buffer createBufferFromHost(ByteBuffer data, MemoryAccess access); + Buffer createBufferFromHost(ByteBuffer data); public static enum ImageChannelOrder { diff --git a/jme3-core/src/main/java/com/jme3/opencl/Event.java b/jme3-core/src/main/java/com/jme3/opencl/Event.java index ec08d764b..501df2593 100644 --- a/jme3-core/src/main/java/com/jme3/opencl/Event.java +++ b/jme3-core/src/main/java/com/jme3/opencl/Event.java @@ -35,18 +35,9 @@ package com.jme3.opencl; * * @author Sebastian Weiss */ -public final class Event { - private final long event; - - public Event(long event) { - this.event = event; - } +public interface Event { - public void waitForFinished() { - throw new UnsupportedOperationException("not supported yet"); - } + void waitForFinished(); - public boolean isCompleted() { - throw new UnsupportedOperationException("not supported yet"); - } + boolean isCompleted(); } diff --git a/jme3-core/src/main/java/com/jme3/opencl/Kernel.java b/jme3-core/src/main/java/com/jme3/opencl/Kernel.java index 933b153ce..813da8ced 100644 --- a/jme3-core/src/main/java/com/jme3/opencl/Kernel.java +++ b/jme3-core/src/main/java/com/jme3/opencl/Kernel.java @@ -34,30 +34,24 @@ package com.jme3.opencl; import com.jme3.math.Quaternion; import com.jme3.math.Vector2f; import com.jme3.math.Vector4f; +import java.nio.ByteBuffer; /** * * @author Sebastian Weiss */ -public final class Kernel { -/* +public abstract class Kernel { private final WorkSize globalWorkSize; private final WorkSize workGroupSize; - private final long kernel; - public Kernel(long kernel) { - this.kernel = kernel; + protected Kernel() { this.globalWorkSize = new WorkSize(0); this.workGroupSize = new WorkSize(0); } - public String getName() { - throw new UnsupportedOperationException("not supported yet"); - } + public abstract String getName(); - public int getArgCount() { - throw new UnsupportedOperationException("not supported yet"); - } + public abstract int getArgCount(); public WorkSize getGlobalWorkSize() { return globalWorkSize; @@ -103,55 +97,31 @@ public final class Kernel { workGroupSize.set(1, 0); } - public void setArg(int index, LocalMemPerElement t) { - throw new UnsupportedOperationException("not supported yet"); - } + public abstract void setArg(int index, LocalMemPerElement t); - public void setArg(int index, LocalMem t) { - throw new UnsupportedOperationException("not supported yet"); - } + public abstract void setArg(int index, LocalMem t); - public void setArg(int index, Buffer t) { - throw new UnsupportedOperationException("not supported yet"); - } + public abstract void setArg(int index, Buffer t); - public void setArg(int index, byte b) { - throw new UnsupportedOperationException("not supported yet"); - } + public abstract void setArg(int index, byte b); - public void setArg(int index, short s) { - throw new UnsupportedOperationException("not supported yet"); - } + public abstract void setArg(int index, short s); - public void setArg(int index, int i) { - throw new UnsupportedOperationException("not supported yet"); - } + public abstract void setArg(int index, int i); - public void setArg(int index, long l) { - throw new UnsupportedOperationException("not supported yet"); - } + public abstract void setArg(int index, long l); - public void setArg(int index, float f) { - throw new UnsupportedOperationException("not supported yet"); - } + public abstract void setArg(int index, float f); - public void setArg(int index, double d) { - throw new UnsupportedOperationException("not supported yet"); - } + public abstract void setArg(int index, double d); - public void setArg(int index, Vector2f v) { - throw new UnsupportedOperationException("not supported yet"); - } + public abstract void setArg(int index, Vector2f v); + + public abstract void setArg(int index, Vector4f v); - //Vector3f not supported because cl_float3 is the same as a float4 - - public void setArg(int index, Vector4f v) { - throw new UnsupportedOperationException("not supported yet"); - } - - public void setArg(int index, Quaternion q) { - throw new UnsupportedOperationException("not supported yet"); - } + public abstract void setArg(int index, Quaternion q); + + public abstract void setArg(int index, ByteBuffer buffer, long size); public void setArg(int index, Object arg) { if (arg instanceof Byte) { @@ -189,9 +159,7 @@ public final class Kernel { } } - public Event Run(CommandQueue queue) { - throw new UnsupportedOperationException("not supported yet"); - } + public abstract Event Run(CommandQueue queue); public Event Run1(CommandQueue queue, WorkSize globalWorkSize, Object... args) { setGlobalWorkSize(globalWorkSize); @@ -208,9 +176,4 @@ public final class Kernel { return Run(queue); } - @Override - public void deleteObject() { - throw new UnsupportedOperationException("Not supported yet."); - } -*/ } diff --git a/jme3-core/src/main/java/com/jme3/opencl/OpenCLException.java b/jme3-core/src/main/java/com/jme3/opencl/OpenCLException.java index b0603b36a..5cfe47a9a 100644 --- a/jme3-core/src/main/java/com/jme3/opencl/OpenCLException.java +++ b/jme3-core/src/main/java/com/jme3/opencl/OpenCLException.java @@ -35,7 +35,7 @@ package com.jme3.opencl; * * @author Sebastian Weiss */ -public class OpenCLException extends Exception { +public class OpenCLException extends RuntimeException { private final int errorCode; diff --git a/jme3-core/src/main/java/com/jme3/opencl/Program.java b/jme3-core/src/main/java/com/jme3/opencl/Program.java index 74c5d5662..32c594a04 100644 --- a/jme3-core/src/main/java/com/jme3/opencl/Program.java +++ b/jme3-core/src/main/java/com/jme3/opencl/Program.java @@ -35,22 +35,11 @@ package com.jme3.opencl; * * @author Sebastian Weiss */ -public final class Program { - private final long program; - - public Program(long program) { - this.program = program; - } +public interface Program { - public void build(String args) { - throw new UnsupportedOperationException("not supported yet"); - } - public void build() { - build(null); - } + void build(String args) throws KernelCompilationException; + void build() throws KernelCompilationException; - public Kernel createKernel(String name) { - throw new UnsupportedOperationException("not supported yet"); - } + Kernel createKernel(String name); } diff --git a/jme3-examples/src/main/java/jme3test/gui/opencl/HelloOpenCL.java b/jme3-examples/src/main/java/jme3test/gui/opencl/HelloOpenCL.java index 66635420f..d653c0934 100644 --- a/jme3-examples/src/main/java/jme3test/gui/opencl/HelloOpenCL.java +++ b/jme3-examples/src/main/java/jme3test/gui/opencl/HelloOpenCL.java @@ -32,19 +32,24 @@ package jme3test.gui.opencl; -import jme3test.helloworld.*; import com.jme3.app.SimpleApplication; -import com.jme3.material.Material; -import com.jme3.math.ColorRGBA; -import com.jme3.math.Vector3f; -import com.jme3.scene.Geometry; -import com.jme3.scene.shape.Box; +import com.jme3.font.BitmapFont; +import com.jme3.font.BitmapText; +import com.jme3.opencl.Buffer; +import com.jme3.opencl.CommandQueue; +import com.jme3.opencl.Context; +import com.jme3.opencl.Event; import com.jme3.system.AppSettings; +import com.jme3.util.BufferUtils; +import java.nio.ByteBuffer; +import java.util.logging.Level; +import java.util.logging.Logger; /** Sample 1 - how to get started with the most simple JME 3 application. * Display a blue 3D cube and view from all sides by * moving the mouse and pressing the WASD keys. */ 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(); @@ -56,12 +61,80 @@ public class HelloOpenCL extends SimpleApplication { @Override public void simpleInitApp() { - Box b = new Box(1, 1, 1); // create cube shape - Geometry geom = new Geometry("Box", b); // create cube geometry from the shape - Material mat = new Material(assetManager, - "Common/MatDefs/Misc/Unshaded.j3md"); // create a simple material - mat.setColor("Color", ColorRGBA.Blue); // set color of material to blue - geom.setMaterial(mat); // set the cube's material - rootNode.attachChild(geom); // make the cube appear in the scene + 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)); + + BitmapText txt1 = new BitmapText(fnt); + txt1.setText(str.toString()); + txt1.setLocalTranslation(5, settings.getHeight() - 5, 0); + guiNode.attachChild(txt1); + } + + 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(); + if (b != (byte)i) { + System.err.println("Wrong byte read: expected="+i+", actual="+b); + return false; + } + } + + //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 devices; - public LwjglContext(CLContext context) { + public LwjglContext(CLContext context, List devices) { this.context = context; + this.devices = devices; } public CLContext getContext() { @@ -55,37 +57,50 @@ public class LwjglContext implements Context { @Override public List getDevices() { - throw new UnsupportedOperationException("Not supported yet."); + return devices; } @Override public CommandQueue createQueue() { - throw new UnsupportedOperationException("Not supported yet."); + return createQueue(devices.get(0)); } @Override + @SuppressWarnings("element-type-mismatch") public CommandQueue createQueue(Device device) { - throw new UnsupportedOperationException("Not supported yet."); + 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(int size, MemoryAccess access) { - throw new UnsupportedOperationException("Not supported yet."); + long flags = Utils.getMemoryAccessFlags(access); + CLMem mem = CL10.clCreateBuffer(context, flags, (long) size, Utils.errorBuffer); + Utils.checkError(Utils.errorBuffer, "clCreateBuffer"); + return new LwjglBuffer(mem); } @Override public Buffer createBuffer(int size) { - throw new UnsupportedOperationException("Not supported yet."); + return createBuffer(size, MemoryAccess.READ_WRITE); } @Override - public Buffer useHostBuffer(ByteBuffer data, int size, MemoryAccess access) { - throw new UnsupportedOperationException("Not supported yet."); + 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 Buffer useHostBuffer(ByteBuffer data, int size) { - throw new UnsupportedOperationException("Not supported yet."); + public Buffer createBufferFromHost(ByteBuffer data) { + return createBufferFromHost(data, MemoryAccess.READ_WRITE); } @Override 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..ebe0ca458 --- /dev/null +++ b/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglEvent.java @@ -0,0 +1,71 @@ +/* + * 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 org.lwjgl.opencl.CL10; +import org.lwjgl.opencl.CLEvent; + +/** + * + * @author Sebastian Weiss + */ +public class LwjglEvent implements Event { + private final CLEvent event; + + public LwjglEvent(CLEvent event) { + this.event = event; + } + + public CLEvent getEvent() { + return event; + } + + @Override + public void waitForFinished() { + CL10.clWaitForEvents(event); + } + + @Override + public boolean isCompleted() { + int status = event.getInfoInt(CL10.CL_EVENT_COMMAND_EXECUTION_STATUS); + if (status == CL10.CL_SUCCESS) { + return true; + } else if (status < 0) { + Utils.checkError(status, "EventStatus"); + return false; + } else { + return false; + } + } + +} diff --git a/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/Utils.java b/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/Utils.java index 985d60ad3..89509c0ec 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/Utils.java +++ b/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/Utils.java @@ -31,6 +31,17 @@ */ package com.jme3.opencl.lwjgl; +import com.jme3.opencl.MappingAccess; +import com.jme3.opencl.MemoryAccess; +import com.jme3.opencl.OpenCLException; +import java.nio.*; +import org.lwjgl.BufferUtils; +import org.lwjgl.LWJGLUtil; +import org.lwjgl.PointerBuffer; +import org.lwjgl.opencl.CL10; +import org.lwjgl.opencl.CL12; +import org.lwjgl.opencl.Util; + /** * * @author Sebastian Weiss @@ -48,4 +59,70 @@ public class Utils { 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 TempBuffer[] tempBuffers = new TempBuffer[8]; + public static 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); + } + + 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) { + //TODO: proper handling + Util.checkCLError(error); + } + + 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 02bdd281a..32767fa9b 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,10 +35,8 @@ 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.Context; import com.jme3.opencl.Device; import com.jme3.opencl.PlatformChooser; -import com.jme3.opencl.lwjgl.LwjglCL; import com.jme3.opencl.lwjgl.LwjglDevice; import com.jme3.opencl.lwjgl.LwjglPlatform; import com.jme3.opencl.lwjgl.PlatformChooserImpl; @@ -261,6 +259,7 @@ public abstract class LwjglContext implements JmeContext { } + @SuppressWarnings("unchecked") protected void initOpenCL() { logger.info("Initialize OpenCL wiht LWJGL2"); @@ -349,7 +348,8 @@ public abstract class LwjglContext implements JmeContext { //create context try { - clContext = new com.jme3.opencl.lwjgl.LwjglContext(CLContext.create(platform.getPlatform(), devices, null, Display.getDrawable(), null)); + 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;