aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/com/mbien/opencl/CLContext.java11
-rw-r--r--src/com/mbien/opencl/CLException.java11
-rw-r--r--src/com/mbien/opencl/CLProgram.java66
-rw-r--r--src/com/mbien/opencl/InternalBufferUtil.java39
-rw-r--r--src/com/mbien/opencl/QueueBarrier.java2
-rw-r--r--test/com/mbien/opencl/CLProgramTest.java91
-rw-r--r--test/com/mbien/opencl/HighLevelBindingTest.java45
7 files changed, 210 insertions, 55 deletions
diff --git a/src/com/mbien/opencl/CLContext.java b/src/com/mbien/opencl/CLContext.java
index 6fbad0df..19e3ef09 100644
--- a/src/com/mbien/opencl/CLContext.java
+++ b/src/com/mbien/opencl/CLContext.java
@@ -214,6 +214,13 @@ public class CLContext implements CLResource {
return createProgram(sb.toString());
}
+
+ public CLProgram createProgram(Map<CLDevice, byte[]> binaries) {
+ CLProgram program = new CLProgram(this, binaries);
+ programs.add(program);
+ return program;
+ }
+
/**
* Creates a CLBuffer with the specified flags and element count. No flags creates a MEM.READ_WRITE buffer.
*/
@@ -379,8 +386,8 @@ public class CLContext implements CLResource {
}
/**
- * Returns the device with maximal FLOPS and the specified type from this context.
- * The device speed is estimated by calulating the product of
+ * Returns the device with maximal FLOPS of the specified device type from this context.
+ * The device speed is estimated by calculating the product of
* MAX_COMPUTE_UNITS and MAX_CLOCK_FREQUENCY.
*/
public CLDevice getMaxFlopsDevice(CLDevice.Type type) {
diff --git a/src/com/mbien/opencl/CLException.java b/src/com/mbien/opencl/CLException.java
index 35c539c4..3703f235 100644
--- a/src/com/mbien/opencl/CLException.java
+++ b/src/com/mbien/opencl/CLException.java
@@ -3,7 +3,7 @@ package com.mbien.opencl;
import static com.mbien.opencl.CL.*;
/**
- * Main Exception type for runtime OpenCL errors and unsuccessful function calls (e.g. returning other values than CL_SUCCESS).
+ * Main Exception type for runtime OpenCL errors and unsuccessful function calls (e.g. returning not CL_SUCCESS).
* @author Michael Bien
*/
public class CLException extends RuntimeException {
@@ -17,11 +17,20 @@ public class CLException extends RuntimeException {
errorcode = error;
}
+ /**
+ * Throws a CLException when <code>status != CL_SUCCESS</code>.
+ */
public static final void checkForError(int status, String message) {
if(status != CL_SUCCESS)
throw new CLException(status, message);
}
+ /**
+ * Returns a human readable String for the OpenCL error code.
+ */
+ public String getCLErrorString() {
+ return identifyError(errorcode);
+ }
private static final String identifyError(int error) {
diff --git a/src/com/mbien/opencl/CLProgram.java b/src/com/mbien/opencl/CLProgram.java
index 9e141dde..306a5b66 100644
--- a/src/com/mbien/opencl/CLProgram.java
+++ b/src/com/mbien/opencl/CLProgram.java
@@ -1,5 +1,6 @@
package com.mbien.opencl;
+import com.sun.gluegen.runtime.BufferFactory;
import com.sun.gluegen.runtime.CPU;
import com.sun.gluegen.runtime.PointerBuffer;
import java.nio.ByteBuffer;
@@ -11,6 +12,7 @@ import java.util.Map;
import static com.mbien.opencl.CLException.*;
import static com.mbien.opencl.CL.*;
+import java.util.Set;
/**
*
@@ -33,18 +35,55 @@ public class CLProgram implements CLResource {
this.cl = context.cl;
this.context = context;
- IntBuffer ib = ByteBuffer.allocateDirect(4).order(ByteOrder.nativeOrder()).asIntBuffer();
+ IntBuffer ib = BufferFactory.newDirectByteBuffer(4).asIntBuffer();
// Create the program
ID = cl.clCreateProgramWithSource(context.ID, 1, new String[] {src},
PointerBuffer.allocateDirect(1).put(src.length()), ib);
checkForError(ib.get(), "can not create program with source");
}
+ CLProgram(CLContext context, Map<CLDevice, byte[]> binaries) {
+
+ this.cl = context.cl;
+ this.context = context;
+
+ PointerBuffer devices = PointerBuffer.allocateDirect(binaries.size());
+ PointerBuffer lengths = PointerBuffer.allocateDirect(binaries.size());
+ ByteBuffer[] codeBuffers = new ByteBuffer[binaries.size()];
+
+ int i = 0;
+ Set<CLDevice> keys = binaries.keySet();
+ for (CLDevice device : keys) {
+
+ byte[] bytes = binaries.get(device);
+
+ devices.put(device.ID);
+ lengths.put(bytes.length);
+
+ codeBuffers[i] = BufferFactory.newDirectByteBuffer(bytes.length).put(bytes);
+ codeBuffers[i].rewind();
+ i++;
+ }
+ devices.rewind();
+ lengths.rewind();
+
+ IntBuffer err = BufferFactory.newDirectByteBuffer(4).asIntBuffer();
+// IntBuffer status = BufferFactory.newDirectByteBuffer(binaries.size()*4).asIntBuffer();
+ ID = cl.clCreateProgramWithBinary(context.ID, devices.capacity(), devices, lengths, codeBuffers, /*status*/null, err);
+
+// while(status.remaining() != 0) {
+// checkForError(status.get(), "unable to load binaries on all devices");
+// }
+
+ checkForError(err.get(), "can not create program with binary");
+
+ }
+
private final void initKernels() {
if(kernels == null) {
- IntBuffer numKernels = ByteBuffer.allocateDirect(4).order(ByteOrder.nativeOrder()).asIntBuffer();
+ IntBuffer numKernels = BufferFactory.newDirectByteBuffer(4).asIntBuffer();
int ret = cl.clCreateKernelsInProgram(ID, 0, null, numKernels);
checkForError(ret, "can not create kernels for program");
@@ -348,16 +387,27 @@ public class CLProgram implements CLResource {
int ret = cl.clGetProgramInfo(ID, CL_PROGRAM_BINARY_SIZES, sizes.capacity(), sizes, null);
checkForError(ret, "on clGetProgramInfo");
- int binarySize = 0;
- while(sizes.remaining() != 0)
- binarySize += (int)sizes.getLong();
+ int binariesSize = 0;
+ while(sizes.remaining() != 0) {
+ int size = (int) sizes.getLong();
+ binariesSize += size;
+ }
+ ByteBuffer binaries = ByteBuffer.allocateDirect(binariesSize).order(ByteOrder.nativeOrder());
- ByteBuffer binaries = ByteBuffer.allocateDirect(binarySize).order(ByteOrder.nativeOrder());
- ret = cl.clGetProgramInfo(ID, CL_PROGRAM_BINARIES, binaries.capacity(), binaries, null); // TODO crash, driver bug?
+
+ long address = InternalBufferUtil.getDirectBufferAddress(binaries);
+ PointerBuffer addresses = PointerBuffer.allocateDirect(sizes.capacity());
+ sizes.rewind();
+ while(sizes.remaining() != 0) {
+ addresses.put(address);
+ address += sizes.getLong();
+ }
+
+ ret = cl.clGetProgramInfo(ID, CL_PROGRAM_BINARIES, addresses.capacity(), addresses.getBuffer(), null);
checkForError(ret, "on clGetProgramInfo");
Map<CLDevice, byte[]> map = new HashMap<CLDevice, byte[]>();
-
+ sizes.rewind();
for (int i = 0; i < devices.length; i++) {
byte[] bytes = new byte[(int)sizes.getLong()];
binaries.get(bytes);
diff --git a/src/com/mbien/opencl/InternalBufferUtil.java b/src/com/mbien/opencl/InternalBufferUtil.java
new file mode 100644
index 00000000..a2573784
--- /dev/null
+++ b/src/com/mbien/opencl/InternalBufferUtil.java
@@ -0,0 +1,39 @@
+package com.mbien.opencl;
+
+import java.lang.reflect.Field;
+import java.nio.Buffer;
+import sun.misc.Unsafe;
+
+/**
+ *
+ * @author Michael Bien
+ */
+class InternalBufferUtil {
+
+ private static final long addressFieldOffset;
+ private static Unsafe unsafe;
+
+ static {
+ try {
+ Field f = Buffer.class.getDeclaredField("address");
+
+ Field[] fields = Unsafe.class.getDeclaredFields();
+ for (int i = 0; i < fields.length; i++) {
+ if (fields[i].getName().equals("theUnsafe")) {
+ fields[i].setAccessible(true);
+ unsafe = (Unsafe)fields[i].get(Unsafe.class);
+ break;
+ }
+ }
+
+ addressFieldOffset = unsafe.objectFieldOffset(f);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static long getDirectBufferAddress(Buffer buffer) {
+ return ((buffer == null) ? 0 : unsafe.getLong(buffer, addressFieldOffset));
+ }
+
+} \ No newline at end of file
diff --git a/src/com/mbien/opencl/QueueBarrier.java b/src/com/mbien/opencl/QueueBarrier.java
index d3a5ab2e..73339192 100644
--- a/src/com/mbien/opencl/QueueBarrier.java
+++ b/src/com/mbien/opencl/QueueBarrier.java
@@ -67,7 +67,7 @@ public class QueueBarrier {
}
/**
- * @see {@link #await()}
+ * @see #await()
* @param timeout the maximum time to wait
* @param unit the time unit of the {@code timeout} argument
*/
diff --git a/test/com/mbien/opencl/CLProgramTest.java b/test/com/mbien/opencl/CLProgramTest.java
new file mode 100644
index 00000000..0ca0fa9e
--- /dev/null
+++ b/test/com/mbien/opencl/CLProgramTest.java
@@ -0,0 +1,91 @@
+package com.mbien.opencl;
+
+import java.io.IOException;
+import java.util.Map;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+import static java.lang.System.*;
+import static com.mbien.opencl.TestUtils.*;
+import static com.sun.gluegen.runtime.BufferFactory.*;
+
+/**
+ *
+ * @author Michael Bien
+ */
+public class CLProgramTest {
+
+ @Test
+ public void rebuildProgramTest() throws IOException {
+
+ out.println(" - - - CLProgramTest; rebuild program test - - - ");
+
+ CLContext context = CLContext.create();
+ CLProgram program = context.createProgram(getClass().getResourceAsStream("testkernels.cl"));
+
+ try{
+ program.getCLKernels();
+ fail("expected exception but got none :(");
+ }catch(CLException ex) {
+ out.println("got expected exception: "+ex.getCLErrorString());
+ assertEquals(ex.errorcode, CL.CL_INVALID_PROGRAM_EXECUTABLE);
+ }
+
+ program.build();
+ assertTrue(program.isExecutable());
+ out.println(program.getBuildStatus());
+
+ Map<String, CLKernel> kernels = program.getCLKernels();
+ assertNotNull(kernels);
+ assertTrue("kernel map is empty", kernels.size() > 0);
+
+ // rebuild
+ // 1. release kernels (internally)
+ // 2. build program
+ program.build();
+ assertTrue(program.isExecutable());
+ out.println(program.getBuildStatus());
+
+ // try again with rebuilt program
+ kernels = program.getCLKernels();
+ assertNotNull(kernels);
+ assertTrue("kernel map is empty", kernels.size() > 0);
+ assertTrue(kernels.size() > 0);
+
+ context.release();
+ }
+
+ @Test
+ public void programBinariesTest() throws IOException {
+
+ out.println(" - - - CLProgramTest; down-/upload binaries test - - - ");
+
+ CLContext context = CLContext.create();
+ CLProgram program = context.createProgram(getClass().getResourceAsStream("testkernels.cl")).build();
+
+ // optain binaries
+ Map<CLDevice, byte[]> binaries = program.getBinaries();
+ assertFalse(binaries.isEmpty());
+
+ CLDevice[] devices = program.getCLDevices();
+ for (CLDevice device : devices) {
+ assertTrue(binaries.containsKey(device));
+ }
+
+ // 1. release program
+ // 2. re-create program with old binaries
+ program.release();
+
+ program = context.createProgram(binaries);
+
+ out.println(program.getBuildStatus());
+
+ program.build();
+
+ assertTrue(program.isExecutable());
+
+ }
+
+
+
+}
diff --git a/test/com/mbien/opencl/HighLevelBindingTest.java b/test/com/mbien/opencl/HighLevelBindingTest.java
index d28dd042..171c972f 100644
--- a/test/com/mbien/opencl/HighLevelBindingTest.java
+++ b/test/com/mbien/opencl/HighLevelBindingTest.java
@@ -120,8 +120,8 @@ public class HighLevelBindingTest {
assertFalse(source.trim().isEmpty());
// out.println("source:\n"+source);
-// Map<CLDevice, byte[]> binaries = program.getBinaries();
-// assertFalse(binaries.isEmpty());
+ Map<CLDevice, byte[]> binaries = program.getBinaries();
+ assertFalse(binaries.isEmpty());
int elementCount = 11444777; // Length of float arrays to process (odd # for illustration)
int localWorkSize = 256; // set and log Global and Local work size dimensions
@@ -189,46 +189,5 @@ public class HighLevelBindingTest {
context.release();
}
- @Test
- public void rebuildProgramTest() throws IOException {
-
- out.println(" - - - highLevelTest; rebuild program test - - - ");
-
- CLContext context = CLContext.create();
- CLProgram program = context.createProgram(getClass().getResourceAsStream("testkernels.cl"));
-
- try{
- program.getCLKernels();
- fail("expected exception but got none :(");
- }catch(CLException ex) {
- out.println("got expected exception:\n"+ex.getMessage());
- assertEquals(ex.errorcode, CL.CL_INVALID_PROGRAM_EXECUTABLE);
- }
-
- program.build();
- assertTrue(program.isExecutable());
- out.println(program.getBuildStatus());
-
- Map<String, CLKernel> kernels = program.getCLKernels();
- assertNotNull(kernels);
- assertTrue("kernel map is empty", kernels.size() > 0);
-
- // rebuild
- // 1. release kernels (internally)
- // 2. build program
- program.build();
- assertTrue(program.isExecutable());
- out.println(program.getBuildStatus());
-
- // try again with rebuilt program
- kernels = program.getCLKernels();
- assertNotNull(kernels);
- assertTrue("kernel map is empty", kernels.size() > 0);
- assertTrue(kernels.size() > 0);
-
- context.release();
- }
-
-
}