/* * 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; } }