/*
* Copyright (c) 2009 JogAmp Community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY JogAmp Community ``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 JogAmp Community 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.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of JogAmp Community.
*/
package com.jogamp.opencl;
import com.jogamp.opencl.llb.CL;
import com.jogamp.opencl.impl.CLTLAccessorFactory;
import com.jogamp.common.nio.Buffers;
import com.jogamp.common.JogampRuntimeException;
import com.jogamp.common.nio.PointerBuffer;
import com.jogamp.opencl.spi.CLPlatformInfoAccessor;
import com.jogamp.opencl.util.CLUtil;
import com.jogamp.opencl.llb.impl.CLImpl11;
import com.jogamp.opencl.llb.impl.CLImpl12;
import com.jogamp.opencl.llb.impl.CLImpl20;
import com.jogamp.opencl.spi.CLAccessorFactory;
import com.jogamp.opencl.spi.CLInfoAccessor;
import com.jogamp.opencl.util.Filter;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import static com.jogamp.opencl.CLException.*;
import static com.jogamp.opencl.llb.CL.*;
/**
* CLPlatfrorm representing a OpenCL implementation (e.g. graphics driver).
*
* optional eager initialization:
*
* if( !CLPlatform.isAvailable() ) {
* return; // abort
* }
* try{
* CLPlatform.initialize();
* }catch(JogampRuntimeException ex) {
* throw new RuntimeException("could not load Java OpenCL Binding");
* }
*
*
* Example initialization:
*
* if( !CLPlatform.isAvailable() ) {
* return; // abort
* }
* CLPlatform platform = CLPlatform.getDefault(type(GPU));
*
* if(platform == null) {
* throw new RuntimeException("please update your graphics drivers");
* }
*
* CLContext context = CLContext.create(platform.getMaxFlopsDevice());
* try {
* // use it
* }finally{
* context.release();
* }
*
* concurrency:
* CLPlatform is threadsafe.
*
* @author Michael Bien, et al.
* @see #isAvailable()
* @see #initialize()
* @see #getDefault()
* @see #listCLPlatforms()
*/
public class CLPlatform {
/**
* OpenCL platform id for this platform.
*/
public final long ID;
/**
* Version of this OpenCL platform.
*/
public final CLVersion version;
protected static CL cl;
private static CLAccessorFactory defaultFactory;
private final CLAccessorFactory factory;
private Set extensions;
protected final CLPlatformInfoAccessor info;
private CLPlatform(final long id) {
this(id, null);
}
protected CLPlatform(final long id, final CLAccessorFactory factory) {
initialize();
this.ID = id;
if(factory == null) {
this.factory = defaultFactory;
}else{
this.factory = factory;
}
this.info = this.factory.createPlatformInfoAccessor(cl, id);
this.version = new CLVersion(getInfoString(CL_PLATFORM_VERSION));
}
/**
* @returns true if OpenCL is available on this machine,
* i.e. all native libraries could be loaded (CL and CL/JNI).
*/
public static boolean isAvailable() { return CLImpl11.isAvailable(); }
/**
* Eagerly initializes JOCL. Subsequent calls do nothing.
* @throws JogampRuntimeException if something went wrong in the initialization (e.g. OpenCL lib not found).
* @see #isAvailable()
*/
public static void initialize() throws JogampRuntimeException {
initialize(null);
}
// keep package private until SPI is stablized
/**
* Eagerly initializes JOCL. Subsequent calls do nothing.
* @param factory CLAccessorFactory used for creating the bindings.
* @throws JogampRuntimeException if something went wrong in the initialization (e.g. OpenCL lib not found).
* @see #isAvailable()
*/
synchronized static void initialize(final CLAccessorFactory factory) throws JogampRuntimeException {
if(cl != null) {
return;
}
if(defaultFactory == null) {
if(factory == null) {
defaultFactory = new CLTLAccessorFactory();
}else{
defaultFactory = factory;
}
}
if( !CLImpl11.isAvailable() ) {
throw new JogampRuntimeException("JOCL is not available");
}
cl = new CLImpl11();
}
/**
* Returns the default OpenCL platform or null when no platform found.
*/
public static CLPlatform getDefault() {
initialize();
return latest(listCLPlatforms());
}
/**
* Returns the default OpenCL platform or null when no platform found.
*/
public static CLPlatform getDefault(final Filter... filter) {
final CLPlatform[] platforms = listCLPlatforms(filter);
if(platforms.length > 0) {
return latest(platforms);
}else{
return null;
}
}
private static CLPlatform latest(final CLPlatform[] platforms) {
CLPlatform best = platforms[0];
for (final CLPlatform platform : platforms) {
if (platform.version.compareTo(best.version) > 0) {
best = platform;
}
}
return best;
}
/**
* Lists all available OpenCL implementations.
* @throws CLException if something went wrong initializing OpenCL
*/
public static CLPlatform[] listCLPlatforms() {
return listCLPlatforms((Filter[])null);
}
/**
* Lists all available OpenCL implementations. The platforms returned must pass all filters.
* @param filter Acceptance filter for the returned platforms.
* @throws CLException if something went wrong initializing OpenCL
*/
public static CLPlatform[] listCLPlatforms(final Filter... filter) {
initialize();
final IntBuffer ib = Buffers.newDirectIntBuffer(1);
// find all available OpenCL platforms
int ret = cl.clGetPlatformIDs(0, null, ib);
checkForError(ret, "can not enumerate platforms");
// receive platform ids
final PointerBuffer platformId = PointerBuffer.allocateDirect(ib.get(0));
ret = cl.clGetPlatformIDs(platformId.capacity(), platformId, null);
checkForError(ret, "can not enumerate platforms");
final List platforms = new ArrayList();
for (int i = 0; i < platformId.capacity(); i++) {
final CLPlatform platform = new CLPlatform(platformId.get(i));
addIfAccepted(platform, platforms, filter);
}
return platforms.toArray(new CLPlatform[platforms.size()]);
}
/**
* Returns the low level binding interface to the OpenCL APIs. This interface is always for OpenCL 1.1.
*/
public static CL getLowLevelCLInterface() {
initialize();
return cl;
}
/**
* Returns the low level binding interface to the OpenCL APIs for the specified device. This interface
* is the newest one the device supports.
*/
public static CL getLowLevelCLInterfaceForDevice(final long device) {
initialize();
CLInfoAccessor deviceInfo = defaultFactory.createDeviceInfoAccessor(cl, device);
CLVersion version = new CLVersion(deviceInfo.getString(CL_DEVICE_VERSION));
if(version.isEqual(CLVersion.CL_1_2))
return new CLImpl12();
if(version.isEqual(CLVersion.CL_2_0))
return new CLImpl20();
return cl;
}
/**
* Hint to allow the implementation to release the resources allocated by the OpenCL compiler.
* Calls to {@link CLProgram#build()} after unloadCompiler will reload the compiler if necessary.
*/
public static void unloadCompiler() {
initialize();
final int ret = cl.clUnloadCompiler();
checkForError(ret, "error while sending unload compiler hint");
}
/**
* Lists all physical devices available on this platform.
* @see #listCLDevices(com.jogamp.opencl.CLDevice.Type...)
*/
public CLDevice[] listCLDevices() {
return this.listCLDevices(CLDevice.Type.ALL);
}
/**
* Lists all physical devices available on this platform matching the given {@link CLDevice.Type}.
*/
public CLDevice[] listCLDevices(final CLDevice.Type... types) {
initialize();
final List list = new ArrayList();
for(int t = 0; t < types.length; t++) {
final CLDevice.Type type = types[t];
final long[] deviceIDs = info.getDeviceIDs(type.TYPE);
//add device to list
for (int n = 0; n < deviceIDs.length; n++) {
list.add(createDevice(deviceIDs[n]));
}
}
return list.toArray(new CLDevice[list.size()]);
}
/**
* Lists all physical devices available on this platform matching the given {@link Filter}.
*/
public CLDevice[] listCLDevices(final Filter... filters) {
initialize();
final List list = new ArrayList();
final long[] deviceIDs = info.getDeviceIDs(CL_DEVICE_TYPE_ALL);
//add device to list
for (int n = 0; n < deviceIDs.length; n++) {
final CLDevice device = createDevice(deviceIDs[n]);
addIfAccepted(device, list, filters);
}
return list.toArray(new CLDevice[list.size()]);
}
protected CLDevice createDevice(final long id) {
return new CLDevice(this, id);
}
private static void addIfAccepted(final I item, final List list, final Filter[] filters) {
if(filters == null) {
list.add(item);
}else{
boolean accepted = true;
for (final Filter filter : filters) {
if(!filter.accept(item)) {
accepted = false;
break;
}
}
if(accepted) {
list.add(item);
}
}
}
static CLDevice findMaxFlopsDevice(final CLDevice[] devices) {
return findMaxFlopsDevice(devices, null);
}
static CLDevice findMaxFlopsDevice(final CLDevice[] devices, final CLDevice.Type type) {
initialize();
CLDevice maxFLOPSDevice = null;
int maxflops = -1;
for (int i = 0; i < devices.length; i++) {
final CLDevice device = devices[i];
if(type == null || type.equals(device.getType())) {
final int maxComputeUnits = device.getMaxComputeUnits();
final int maxClockFrequency = device.getMaxClockFrequency();
final int flops = maxComputeUnits*maxClockFrequency;
if(flops > maxflops) {
maxflops = flops;
maxFLOPSDevice = device;
}
}
}
return maxFLOPSDevice;
}
/**
* Returns the device with maximal FLOPS from this platform.
* The device speed is estimated by calculating the product of
* MAX_COMPUTE_UNITS and MAX_CLOCK_FREQUENCY.
* @see #getMaxFlopsDevice(com.jogamp.opencl.CLDevice.Type...)
*/
public CLDevice getMaxFlopsDevice() {
return findMaxFlopsDevice(listCLDevices());
}
/**
* Returns the device with maximal FLOPS and the specified type from this platform.
* The device speed is estimated by calculating the product of
* MAX_COMPUTE_UNITS and MAX_CLOCK_FREQUENCY.
*/
public CLDevice getMaxFlopsDevice(final CLDevice.Type... types) {
return findMaxFlopsDevice(listCLDevices(types));
}
/**
* Returns the device with maximal FLOPS and the specified type from this platform.
* The device speed is estimated by calculating the product of
* MAX_COMPUTE_UNITS and MAX_CLOCK_FREQUENCY.
*/
public CLDevice getMaxFlopsDevice(final Filter... filter) {
return findMaxFlopsDevice(listCLDevices(filter));
}
/**
* Returns the platform name.
*/
@CLProperty("CL_PLATFORM_NAME")
public String getName() {
return getInfoString(CL_PLATFORM_NAME);
}
/**
* Returns the OpenCL version supported by this platform.
*/
@CLProperty("CL_PLATFORM_VERSION")
public CLVersion getVersion() {
return version;
}
/**
* Returns the OpenCL Specification version supported by this platform.
*/
public String getSpecVersion() {
return version.getSpecVersion();
}
/**
* @see CLVersion#isAtLeast(com.jogamp.opencl.CLVersion)
*/
public boolean isAtLeast(final CLVersion other) {
return version.isAtLeast(other);
}
/**
* @see CLVersion#isAtLeast(int, int)
*/
public boolean isAtLeast(final int major, final int minor) {
return version.isAtLeast(major, minor);
}
/**
* Returns the platform profile.
*/
@CLProperty("CL_PLATFORM_PROFILE")
public String getProfile() {
return getInfoString(CL_PLATFORM_PROFILE);
}
/**
* Returns the platform vendor.
*/
@CLProperty("CL_PLATFORM_VENDOR")
public String getVendor() {
return getInfoString(CL_PLATFORM_VENDOR);
}
/**
* @return true if the vendor is AMD.
*/
public boolean isVendorAMD() {
return getVendor().contains("Advanced Micro Devices");
}
/**
* @return true if the vendor is Intel.
*/
public boolean isVendorIntel() {
return getVendor().contains("Intel");
}
/**
* Returns the ICD suffix.
*/
@CLProperty("CL_PLATFORM_ICD_SUFFIX_KHR")
public String getICDSuffix() {
return getInfoString(CL_PLATFORM_ICD_SUFFIX_KHR);
}
/**
* Returns true if the extension is supported on this platform.
*/
public boolean isExtensionAvailable(final String extension) {
return getExtensions().contains(extension);
}
/**
* Returns all platform extension names as unmodifiable Set.
*/
@CLProperty("CL_PLATFORM_EXTENSIONS")
public synchronized Set getExtensions() {
if(extensions == null) {
extensions = new HashSet();
final String ext = getInfoString(CL_PLATFORM_EXTENSIONS);
final Scanner scanner = new Scanner(ext);
while(scanner.hasNext())
extensions.add(scanner.next());
scanner.close();
extensions = Collections.unmodifiableSet(extensions);
}
return extensions;
}
/**
* Returns a Map of platform properties with the enum names as keys.
* @see CLUtil#obtainPlatformProperties(com.jogamp.opencl.CLPlatform)
*/
public Map getProperties() {
return CLUtil.obtainPlatformProperties(this);
}
/**
* Returns a info string in exchange for a key (CL_PLATFORM_*).
*/
public final String getInfoString(final int key) {
return info.getString(key);
}
final CLAccessorFactory getAccessorFactory(){
return factory;
}
public final CLPlatformInfoAccessor getCLAccessor(){
return info;
}
protected CL getCLBinding() {
return cl;
}
@Override
public String toString() {
return getClass().getSimpleName()+" [name: " + getName()
+", vendor: "+getVendor()
+", profile: "+getProfile()
+", version: "+getVersion()+"]";
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final CLPlatform other = (CLPlatform) obj;
if (this.ID != other.ID) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 7;
hash = 71 * hash + (int) (this.ID ^ (this.ID >>> 32));
return hash;
}
}