added querying of the program binaries and building the programs from these binaries.

TestVertexBufferSharing shows how this is used to build a simple program cache.
define_list_fix
shamanDevel 9 years ago
parent 54113f35e0
commit 22307257e0
  1. 18
      jme3-core/src/main/java/com/jme3/opencl/Context.java
  2. 27
      jme3-core/src/main/java/com/jme3/opencl/Program.java
  3. 61
      jme3-examples/src/main/java/jme3test/opencl/TestVertexBufferSharing.java
  4. 11
      jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglContext.java
  5. 33
      jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglProgram.java

@ -367,4 +367,22 @@ public abstract class Context extends AbstractOpenCLObject {
public Program createProgramFromSourceFiles(AssetManager assetManager, List<String> resources) {
return createProgramFromSourceFilesWithInclude(assetManager, "", resources);
}
/**
* Creates a program from the specified binaries.
* The binaries are created by {@link Program#getBinary(com.jme3.opencl.Device) }.
* The returned program still needs to be build using
* {@link Program#build(java.lang.String, com.jme3.opencl.Device...) }.
* <b>Important:</b>The device passed to {@code Program.getBinary(..)},
* this method and {@code Program#build(..)} must be the same.
*
* The binaries are used to build a program cache across multiple launches
* of the application. The programs build mach faster from binaries than
* from sources.
*
* @param binaries the binaries
* @param device the device to use
* @return the new program
*/
public abstract Program createProgramFromBinary(ByteBuffer binaries, Device device);
}

@ -31,6 +31,8 @@
*/
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.
@ -49,21 +51,27 @@ public abstract class Program extends AbstractOpenCLObject {
}
/**
* Builds this program with the specified argument string.
* 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
* 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) throws KernelCompilationException;
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("");
build("", null);
}
/**
@ -82,4 +90,15 @@ public abstract class Program extends AbstractOpenCLObject {
*/
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);
}

@ -40,11 +40,19 @@ import com.jme3.scene.Geometry;
import com.jme3.scene.VertexBuffer;
import com.jme3.scene.shape.Box;
import com.jme3.system.AppSettings;
import com.jme3.system.JmeSystem;
import com.jme3.util.BufferUtils;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This test class tests the capability to read and modify an OpenGL vertex buffer.
*
* It is also shown how to use the program binaries to implement a simple program
* cache.
* @author shaman
*/
public class TestVertexBufferSharing extends SimpleApplication {
@ -106,19 +114,48 @@ public class TestVertexBufferSharing extends SimpleApplication {
private void initOpenCL1() {
clContext = context.getOpenCLContext();
clQueue = clContext.createQueue();
Device device = clContext.getDevices().get(0);
clQueue = clContext.createQueue(device);
clQueue.register();
//create kernel
String source = ""
+ "__kernel void ScaleKernel(__global float* vb, float scale)\n"
+ "{\n"
+ " int idx = get_global_id(0);\n"
+ " float3 pos = vload3(idx, vb);\n"
+ " pos *= scale;\n"
+ " vstore3(pos, idx, vb);\n"
+ "}\n";
Program program = clContext.createProgramFromSourceCode(source);
program.build();
Program program = null;
File tmpFolder = JmeSystem.getStorageFolder();
File binaryFile = new File(tmpFolder, getClass().getSimpleName()+".clc");
try {
//attempt to load cached binary
byte[] bytes = Files.readAllBytes(binaryFile.toPath());
ByteBuffer bb = BufferUtils.createByteBuffer(bytes);
program = clContext.createProgramFromBinary(bb, device);
program.build();
LOG.info("reuse program from cached binaries");
} catch (java.nio.file.NoSuchFileException ex) {
//do nothing, cache was not created yet
} catch (Exception ex) {
LOG.log(Level.INFO, "Unable to use cached program binaries", ex);
}
if (program == null) {
//build from sources
String source = ""
+ "__kernel void ScaleKernel(__global float* vb, float scale)\n"
+ "{\n"
+ " int idx = get_global_id(0);\n"
+ " float3 pos = vload3(idx, vb);\n"
+ " pos *= scale;\n"
+ " vstore3(pos, idx, vb);\n"
+ "}\n";
program = clContext.createProgramFromSourceCode(source);
program.build();
//Save binary
ByteBuffer bb = program.getBinary(device);
byte[] bytes = new byte[bb.remaining()];
bb.get(bytes);
try {
Files.write(binaryFile.toPath(), bytes);
} catch (IOException ex) {
LOG.log(Level.SEVERE, "Unable to save program binaries", ex);
}
LOG.info("create new program from sources");
}
program.register();
kernel = program.createKernel("ScaleKernel");
kernel.register();

@ -208,6 +208,17 @@ public class LwjglContext extends Context {
return new LwjglProgram(p, this);
}
@Override
public Program createProgramFromBinary(ByteBuffer binaries, Device device) {
Utils.errorBuffer.rewind();
Utils.tempBuffers[0].b16i.rewind();
CLProgram p = CL10.clCreateProgramWithBinary(context, ((LwjglDevice) device).device,
binaries, Utils.tempBuffers[0].b16i, Utils.errorBuffer);
Utils.checkError(Utils.errorBuffer, "clCreateProgramWithSource");
Utils.checkError(Utils.tempBuffers[0].b16i, "clCreateProgramWithSource");
return new LwjglProgram(p, this);
}
private static class ReleaserImpl implements ObjectReleaser {
private CLContext context;
private final List<LwjglDevice> devices;

@ -31,10 +31,8 @@
*/
package com.jme3.opencl.lwjgl;
import com.jme3.opencl.Kernel;
import com.jme3.opencl.KernelCompilationException;
import com.jme3.opencl.OpenCLObjectManager;
import com.jme3.opencl.Program;
import com.jme3.opencl.*;
import java.nio.ByteBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.lwjgl.PointerBuffer;
@ -61,8 +59,17 @@ public class LwjglProgram extends Program {
}
@Override
public void build(String args) throws KernelCompilationException {
int ret = CL10.clBuildProgram(program, (PointerBuffer) null, args, null);
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);
@ -104,6 +111,20 @@ public class LwjglProgram extends Program {
return kx;
}
@Override
public ByteBuffer getBinary(Device device) {
ByteBuffer[] binaries = program.getInfoBinaries((ByteBuffer[]) null);
CLDevice[] devices = program.getInfoDevices();
//find the requested one
assert (binaries.length == devices.length);
for (int i=0; i<devices.length; ++i) {
if (((LwjglDevice) device).device == devices[i]) {
return binaries[i];
}
}
throw new com.jme3.opencl.OpenCLException("Program was not built against the specified device "+device);
}
private static class ReleaserImpl implements ObjectReleaser {
private CLProgram program;
private ReleaserImpl(CLProgram program) {

Loading…
Cancel
Save