#parse("main/Header.vm")
package com.nativelibs4java.opencl;
import static com.nativelibs4java.opencl.CLException.error;
import com.nativelibs4java.opencl.CLPlatform.DeviceFeature;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.logging.*;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.nativelibs4java.opencl.library.OpenCLLibrary;
import com.nativelibs4java.opencl.library.IOpenCLLibrary;
import com.nativelibs4java.opencl.library.IOpenCLLibrary.cl_platform_id;
import org.bridj.*;
import org.bridj.ann.Ptr;
import org.bridj.util.ProcessUtils;
import org.bridj.util.StringUtils;
import static org.bridj.Pointer.*;
/**
* Entry point class for the OpenCL4Java Object-oriented wrappers around the OpenCL API.<br>
* @author Olivier Chafik
*/
public class JavaCL {
static final boolean debug = "true".equals(System.getProperty("javacl.debug")) || "1".equals(System.getenv("JAVACL_DEBUG"));
static final boolean verbose = debug || "true".equals(System.getProperty("javacl.verbose")) || "1".equals(System.getenv("JAVACL_VERBOSE"));
static final boolean logCalls = "true".equals(System.getProperty("javacl.logCalls")) || "1".equals(System.getenv("JAVACL_LOG_CALLS"));
static final int minLogLevel = Level.WARNING.intValue();
static final String JAVACL_DEBUG_COMPILER_FLAGS_PROP = "JAVACL_DEBUG_COMPILER_FLAGS";
static List<String> DEBUG_COMPILER_FLAGS;
static boolean shouldLog(Level level) {
return verbose || level.intValue() >= minLogLevel;
}
static boolean log(Level level, String message, Throwable ex) {
if (!shouldLog(level))
return true;
Logger.getLogger(JavaCL.class.getSimpleName()).log(level, message, ex);
return true;
}
static boolean log(Level level, String message) {
log(level, message, null);
return true;
}
static void check(boolean test, String message, Object... formatArgs) {
if (!test) throw new RuntimeException(String.format(message, formatArgs));
}
private static int getPlatformIDs(int count, Pointer<cl_platform_id> out, Pointer<Integer> pCount) {
assert (count == 0) ^ (pCount == null);
assert (count == 0) == (out == null);
if (hasIcd == null || hasIcd.booleanValue()) {
try {
int ret = CL.clIcdGetPlatformIDsKHR(count, getPeer(out), getPeer(pCount));
if (hasIcd == null)
hasIcd = true;
return ret;
} catch (Throwable th) {
hasIcd = false;
return CL.clGetPlatformIDs(count, getPeer(out), getPeer(pCount));
}
} else {
return CL.clGetPlatformIDs(count, getPeer(out), getPeer(pCount));
}
}
@org.bridj.ann.Library("OpenCLProbe")
@org.bridj.ann.Convention(org.bridj.ann.Convention.Style.StdCall)
public static class OpenCLProbeLibrary {
@org.bridj.ann.Optional
public native static synchronized int clGetPlatformIDs(int cl_uint1, Pointer<cl_platform_id > cl_platform_idPtr1, Pointer<Integer > cl_uintPtr1);
@org.bridj.ann.Optional
public native static synchronized int clIcdGetPlatformIDsKHR(int cl_uint1, Pointer<cl_platform_id > cl_platform_idPtr1, Pointer<Integer > cl_uintPtr1);
@org.bridj.ann.Optional
public native static int clGetPlatformInfo(@Ptr long cl_platform_id1, int cl_platform_info1, @Ptr long size_t1, @Ptr long voidPtr1, @Ptr long size_tPtr1);
#declareInfosGetter("infos", "clGetPlatformInfo")
private static int getPlatformIDs(int count, Pointer<cl_platform_id> out, Pointer<Integer> pCount) {
try {
return clIcdGetPlatformIDsKHR(count, out, pCount);
} catch (Throwable th) {
return clGetPlatformIDs(count, out, pCount);
}
}
private static Pointer<cl_platform_id> getPlatformIDs() {
Pointer<Integer> pCount = allocateInt();
error(getPlatformIDs(0, null, pCount));
int nPlats = pCount.getInt();
if (nPlats == 0)
return null;
Pointer<cl_platform_id> ids = allocateTypedPointers(cl_platform_id.class, nPlats);
error(getPlatformIDs(nPlats, ids, null));
return ids;
}
public static boolean hasOpenCL1_0() {
Pointer<cl_platform_id> ids = getPlatformIDs();
if (ids == null)
return false;
for (cl_platform_id id : ids)
if (isOpenCL1_0(id))
return true;
return false;
}
public static boolean isOpenCL1_0(cl_platform_id platform) {
String version = infos.getString(getPeer(platform), OpenCLLibrary.CL_PLATFORM_VERSION);
return version.matches("OpenCL 1\\.0.*");
}
public static boolean isValid() {
try {
Pointer<cl_platform_id> ids = getPlatformIDs();
return ids != null;
} catch (Throwable th) {
return false;
}
}
}
static final IOpenCLLibrary CL;
static Boolean hasIcd;
static {
if (Platform.isLinux()) {
String amdAppBase = "/opt/AMDAPP/lib";
BridJ.addLibraryPath(amdAppBase + "/" + (Platform.is64Bits() ? "x86_64" : "x86"));
BridJ.addLibraryPath(amdAppBase);
}
boolean needsAdditionalSynchronization = false;
{
OpenCLProbeLibrary probe = null;
try {
try {
BridJ.setNativeLibraryActualName("OpenCLProbe", "OpenCL");
BridJ.register();
} catch (Throwable th) {}
probe = new OpenCLProbeLibrary();
if (!probe.isValid()) {
BridJ.unregister(OpenCLProbeLibrary.class);
//BridJ.setNativeLibraryActualName("OpenCLProbe", "OpenCL");
String alt;
if (Platform.is64Bits() && (BridJ.getNativeLibraryFile(alt = "atiocl64") != null || BridJ.getNativeLibraryFile(alt = "amdocl64") != null) ||
BridJ.getNativeLibraryFile(alt = "atiocl32") != null ||
BridJ.getNativeLibraryFile(alt = "atiocl") != null ||
BridJ.getNativeLibraryFile(alt = "amdocl32") != null ||
BridJ.getNativeLibraryFile(alt = "amdocl") != null)
{
log(Level.INFO, "Hacking around ATI's weird driver bugs (using atiocl/amdocl library instead of OpenCL)", null);
BridJ.setNativeLibraryActualName("OpenCL", alt);
}
BridJ.register(OpenCLProbeLibrary.class);
}
if (probe.hasOpenCL1_0()) {
needsAdditionalSynchronization = true;
log(Level.WARNING, "At least one OpenCL platform uses OpenCL 1.0, which is not thread-safe: will use (slower) synchronized low-level bindings.");
}
} finally {
if (probe != null)
BridJ.unregister(OpenCLProbeLibrary.class);
probe = null;
}
}
if (debug) {
String debugArgs = System.getenv(JAVACL_DEBUG_COMPILER_FLAGS_PROP);
if (debugArgs != null)
DEBUG_COMPILER_FLAGS = Arrays.asList(debugArgs.split(" "));
else if (Platform.isMacOSX())
DEBUG_COMPILER_FLAGS = Arrays.asList("-g");
else
DEBUG_COMPILER_FLAGS = Arrays.asList("-O0", "-g");
int pid = ProcessUtils.getCurrentProcessId();
log(Level.INFO, "Debug mode enabled with compiler flags \"" + StringUtils.implode(DEBUG_COMPILER_FLAGS, " ") + "\" (can be overridden with env. var. JAVACL_DEBUG_COMPILER_FLAGS_PROP)");
log(Level.INFO, "You can debug your kernels with GDB using one of the following commands :\n"
+ "\tsudo gdb --tui --pid=" + pid + "\n"
+ "\tsudo ddd --debugger \"gdb --pid=" + pid + "\"\n"
+ "More info here :\n"
+ "\thttp://code.google.com/p/javacl/wiki/DebuggingKernels");
}
Class<? extends OpenCLLibrary> libraryClass = OpenCLLibrary.class;
if (needsAdditionalSynchronization) {
try {
libraryClass = BridJ.subclassWithSynchronizedNativeMethods(libraryClass);
} catch (Throwable ex) {
throw new RuntimeException("Failed to create a synchronized version of the OpenCL API bindings: " + ex, ex);
}
}
BridJ.register(libraryClass);
try {
IOpenCLLibrary cl = libraryClass.newInstance();
CL = logCalls ? wrapWithLogs(cl) : cl;
} catch (Throwable ex) {
throw new RuntimeException("Failed to instantiate library " + libraryClass.getName() + ": " + ex, ex);
}
}
private static IOpenCLLibrary wrapWithLogs(final IOpenCLLibrary cl) {
return (IOpenCLLibrary) Proxy.newProxyInstance(JavaCL.class.getClassLoader(), new Class[] { IOpenCLLibrary.class }, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(Level.WARNING, method.getName() + "(" + StringUtils.implode(args, ", ") + ")");
Object ret = method.invoke(cl, args);
log(Level.WARNING, "\t" + method.getName() + " -> " + ret);
return ret;
}
});
}
/**
* List the OpenCL implementations that contain at least one GPU device.
*/
public static CLPlatform[] listGPUPoweredPlatforms() {
CLPlatform[] platforms = listPlatforms();
List<CLPlatform> out = new ArrayList<CLPlatform>(platforms.length);
for (CLPlatform platform : platforms) {
if (platform.listGPUDevices(true).length > 0)
out.add(platform);
}
return out.toArray(new CLPlatform[out.size()]);
}
/**
* Lists all available OpenCL implementations.
*/
public static CLPlatform[] listPlatforms() {
Pointer<Integer> pCount = allocateInt();
error(getPlatformIDs(0, null, pCount));
int nPlats = pCount.getInt();
if (nPlats == 0)
return new CLPlatform[0];
Pointer<cl_platform_id> ids = allocateTypedPointers(cl_platform_id.class, nPlats);
error(getPlatformIDs(nPlats, ids, null));
CLPlatform[] platforms = new CLPlatform[nPlats];
for (int i = 0; i < nPlats; i++) {
platforms[i] = new CLPlatform(ids.getSizeTAtIndex(i));
}
return platforms;
}
/**
* Creates an OpenCL context formed of the provided devices.<br>
* It is generally not a good idea to create a context with more than one device,
* because much data is shared between all the devices in the same context.
* @param devices devices that are to form the new context
* @return new OpenCL context
*/
public static CLContext createContext(Map<CLPlatform.ContextProperties, Object> contextProperties, CLDevice... devices) {
return devices[0].getPlatform().createContext(contextProperties, devices);
}
/**
* Allows the implementation to release the resources allocated by the OpenCL compiler. <br>
* This is a hint from the application and does not guarantee that the compiler will not be used in the future or that the compiler will actually be unloaded by the implementation. <br>
* Calls to Program.build() after unloadCompiler() will reload the compiler, if necessary, to build the appropriate program executable.
*/
public static void unloadCompiler() {
error(CL.clUnloadCompiler());
}
/**
* Returns the "best" OpenCL device (currently, the one that has the largest amount of compute units).<br>
* For more control on what is to be considered a better device, please use the {@link JavaCL#getBestDevice(CLPlatform.DeviceFeature[]) } variant.<br>
* This is currently equivalent to <code>getBestDevice(MaxComputeUnits)</code>
*/
public static CLDevice getBestDevice() {
return getBestDevice(CLPlatform.DeviceFeature.MaxComputeUnits);
}
/**
* Returns the "best" OpenCL device based on the comparison of the provided prioritized device feature.<br>
* The returned device does not necessarily exhibit the features listed in preferredFeatures, but it has the best ordered composition of them.<br>
* For instance on a system with a GPU and a CPU device, <code>JavaCL.getBestDevice(CPU, MaxComputeUnits)</code> will return the CPU device, but on another system with two GPUs and no CPU device it will return the GPU that has the most compute units.
*/
public static CLDevice getBestDevice(CLPlatform.DeviceFeature... preferredFeatures) {
List<CLDevice> devices = new ArrayList<CLDevice>();
for (CLPlatform platform : listPlatforms())
devices.addAll(Arrays.asList(platform.listAllDevices(true)));
return CLPlatform.getBestDevice(Arrays.asList(preferredFeatures), devices);
}
/**
* Creates an OpenCL context with the "best" device (see {@link JavaCL#getBestDevice() })
*/
public static CLContext createBestContext() {
return createBestContext(DeviceFeature.MaxComputeUnits);
}
/**
* Creates an OpenCL context with the "best" device based on the comparison of the provided
* prioritized device feature (see {@link JavaCL#getBestDevice(CLPlatform.DeviceFeature...) })
*/
public static CLContext createBestContext(CLPlatform.DeviceFeature... preferredFeatures) {
CLDevice device = getBestDevice(preferredFeatures);
return device.getPlatform().createContext(null, device);
}
/**
* Creates an OpenCL context able to share entities with the current OpenGL context.
* @throws RuntimeException if JavaCL is unable to create an OpenGL-shared OpenCL context.
*/
public static CLContext createContextFromCurrentGL() {
RuntimeException first = null;
for (CLPlatform platform : listPlatforms()) {
try {
CLContext ctx = platform.createContextFromCurrentGL();
if (ctx != null)
return ctx;
} catch (RuntimeException ex) {
if (first == null)
first = ex;
}
}
throw new RuntimeException("Failed to create an OpenCL context based on the current OpenGL context", first);
}
static File userJavaCLDir = new File(new File(System.getProperty("user.home")), ".javacl");
static File userCacheDir = new File(userJavaCLDir, "cache");
static synchronized File createTempFile(String prefix, String suffix, String category) {
File dir = new File(userJavaCLDir, category);
dir.mkdirs();
try {
return File.createTempFile(prefix, suffix, dir);
} catch (IOException ex) {
throw new RuntimeException("Failed to create a temporary directory for category '" + category + "' in " + userJavaCLDir + ": " + ex.getMessage(), ex);
}
}
static synchronized File createTempDirectory(String prefix, String suffix, String category) {
File file = createTempFile(prefix, suffix, category);
file.delete();
file.mkdir();
return file;
}
}