001/* 002 * JavaCL - Java API and utilities for OpenCL 003 * http://javacl.googlecode.com/ 004 * 005 * Copyright (c) 2009-2013, Olivier Chafik (http://ochafik.com/) 006 * All rights reserved. 007 * 008 * Redistribution and use in source and binary forms, with or without 009 * modification, are permitted provided that the following conditions are met: 010 * 011 * * Redistributions of source code must retain the above copyright 012 * notice, this list of conditions and the following disclaimer. 013 * * Redistributions in binary form must reproduce the above copyright 014 * notice, this list of conditions and the following disclaimer in the 015 * documentation and/or other materials provided with the distribution. 016 * * Neither the name of Olivier Chafik nor the 017 * names of its contributors may be used to endorse or promote products 018 * derived from this software without specific prior written permission. 019 * 020 * THIS SOFTWARE IS PROVIDED BY OLIVIER CHAFIK AND CONTRIBUTORS ``AS IS'' AND ANY 021 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 022 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 023 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY 024 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 025 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 026 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 027 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 028 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 029 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 030 */ 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050package com.nativelibs4java.opencl; 051 052import com.nativelibs4java.opencl.library.OpenGLContextUtils; 053import com.nativelibs4java.util.EnumValue; 054import com.nativelibs4java.util.EnumValues; 055import static com.nativelibs4java.opencl.library.OpenCLLibrary.*; 056import static com.nativelibs4java.opencl.library.IOpenCLLibrary.*; 057 058import org.bridj.*; 059import org.bridj.ann.*; 060import static org.bridj.Pointer.*; 061 062import java.nio.ByteOrder; 063import java.util.*; 064import java.util.regex.Matcher; 065import java.util.regex.Pattern; 066import java.util.logging.*; 067import java.util.concurrent.ConcurrentHashMap; 068 069import static com.nativelibs4java.opencl.JavaCL.*; 070import static com.nativelibs4java.opencl.CLException.*; 071 072/** 073 * OpenCL implementation entry point. 074 * see {@link JavaCL#listPlatforms() } 075 * @author Olivier Chafik 076 */ 077public class CLPlatform extends CLAbstractEntity { 078 079 CLPlatform(long platform) { 080 super(platform, true); 081 } 082 083 protected static CLInfoGetter infos = new CLInfoGetter() { 084 @Override 085 protected int getInfo(long entity, int infoTypeEnum, long size, Pointer out, Pointer<SizeT> sizeOut) { 086 return CL.clGetPlatformInfo(entity, infoTypeEnum, size, getPeer(out), getPeer(sizeOut)); 087 } 088 }; 089 090 @Override 091 public String toString() { 092 return toString(new StringBuilder()).toString(); 093 } 094 StringBuilder toString(StringBuilder out) { 095 out. 096 append(getName()). 097 append(" {vendor: ").append(getVendor()). 098 append(", version: ").append(getVersion()). 099 append(", profile: ").append(getProfile()). 100 append(", extensions: ").append(Arrays.toString(getExtensions())). 101 append("}"); 102 return out; 103 } 104 105 @Override 106 protected void clear() { 107 } 108 109 /** 110 * Lists all the devices of the platform 111 * @param onlyAvailable if true, only returns devices that are available 112 * see {@link CLPlatform#listDevices(CLDevice.Type, boolean) } 113 */ 114 public CLDevice[] listAllDevices(boolean onlyAvailable) { 115 return listDevices(CLDevice.Type.All, onlyAvailable); 116 } 117 118 /** 119 * Lists all the GPU devices of the platform 120 * @param onlyAvailable if true, only returns GPU devices that are available 121 * see {@link CLPlatform#listDevices(CLDevice.Type, boolean) } 122 */ 123 public CLDevice[] listGPUDevices(boolean onlyAvailable) { 124 try { 125 return listDevices(CLDevice.Type.GPU, onlyAvailable); 126 } catch (CLException ex) { 127 if (ex.getCode() == CL_DEVICE_NOT_FOUND) { 128 return new CLDevice[0]; 129 } 130 throw new RuntimeException("Unexpected OpenCL error", ex); 131 } 132 } 133 134 /** 135 * Lists all the CPU devices of the platform 136 * @param onlyAvailable if true, only returns CPU devices that are available 137 * see {@link CLPlatform#listDevices(CLDevice.Type, boolean) } 138 */ 139 public CLDevice[] listCPUDevices(boolean onlyAvailable) { 140 try { 141 return listDevices(CLDevice.Type.CPU, onlyAvailable); 142 } catch (CLException ex) { 143 if (ex.getCode() == CL_DEVICE_NOT_FOUND) { 144 return new CLDevice[0]; 145 } 146 throw new RuntimeException("Unexpected OpenCL error", ex); 147 } 148 } 149 150 private CLDevice[] getDevices(Pointer<SizeT> ids, boolean onlyAvailable) { 151 int nDevs = (int)ids.getValidElements(); 152 CLDevice[] devices; 153 if (onlyAvailable) { 154 List<CLDevice> list = new ArrayList<CLDevice>(nDevs); 155 for (int i = 0; i < nDevs; i++) { 156 CLDevice device = new CLDevice(this, ids.getSizeTAtIndex(i)); 157 if (device.isAvailable()) { 158 list.add(device); 159 } 160 } 161 devices = list.toArray(new CLDevice[list.size()]); 162 } else { 163 devices = new CLDevice[nDevs]; 164 for (int i = 0; i < nDevs; i++) { 165 devices[i] = new CLDevice(this, ids.getSizeTAtIndex(i)); 166 } 167 } 168 return devices; 169 } 170 171 long[] getContextProps(Map<ContextProperties, Object> contextProperties) { 172 int nContextProperties = contextProperties == null ? 0 : contextProperties.size(); 173 final long[] properties = new long[(nContextProperties + 1) * 2 + 1]; 174 properties[0] = CL_CONTEXT_PLATFORM; 175 properties[1] = getEntity(); 176 int iProp = 2; 177 if (nContextProperties != 0) { 178 for (Map.Entry<ContextProperties, Object> e : contextProperties.entrySet()) { 179 //if (!(v instanceof Number)) throw new IllegalArgumentException("Invalid context property value for '" + e.getKey() + ": " + v); 180 properties[iProp++] = e.getKey().value(); 181 Object v = e.getValue(); 182 if (v instanceof Number) 183 properties[iProp++] = ((Number)v).longValue(); 184 else if (v instanceof Pointer) 185 properties[iProp++] = ((Pointer)v).getPeer(); 186 else 187 throw new IllegalArgumentException("Cannot convert value " + v + " to a context property value !"); 188 } 189 } 190 //properties[iProp] = 0; 191 return properties; 192 } 193 194 /** 195 * Enums used to indicate how to choose the best CLDevice. 196 */ 197 public enum DeviceFeature { 198 /** 199 * Prefer CPU devices (see {@link CLDevice#getType() }) 200 */ 201 CPU { 202 Comparable extractValue(CLDevice device) { 203 return device.getType().contains(CLDevice.Type.CPU) ? 1 : 0; 204 } 205 }, 206 /** 207 * Prefer GPU devices (see {@link CLDevice#getType() }) 208 */ 209 GPU { 210 Comparable extractValue(CLDevice device) { 211 return device.getType().contains(CLDevice.Type.GPU) ? 1 : 0; 212 } 213 }, 214 /** 215 * Prefer Accelerator devices (see {@link CLDevice#getType() }) 216 */ 217 Accelerator { 218 Comparable extractValue(CLDevice device) { 219 return device.getType().contains(CLDevice.Type.Accelerator) ? 1 : 0; 220 } 221 }, 222 /** 223 * Prefer devices with the most compute units (see {@link CLDevice#getMaxComputeUnits() }) 224 */ 225 MaxComputeUnits { 226 Comparable extractValue(CLDevice device) { 227 return device.getMaxComputeUnits(); 228 } 229 }, 230 /** 231 * Prefer devices with the same byte ordering as the hosting platform (see {@link CLDevice#getByteOrder() }) 232 */ 233 NativeEndianness { 234 Comparable extractValue(CLDevice device) { 235 return device.getKernelsDefaultByteOrder() == ByteOrder.nativeOrder() ? 1 : 0; 236 } 237 }, 238 /** 239 * Prefer devices that support double-precision float computations (see {@link CLDevice#isDoubleSupported() }) 240 */ 241 DoubleSupport { 242 Comparable extractValue(CLDevice device) { 243 return device.isDoubleSupported() ? 1 : 0; 244 } 245 }, 246 /** 247 * Prefer devices that support images and with the most supported image formats (see {@link CLDevice#hasImageSupport() }) 248 */ 249 ImageSupport { 250 Comparable extractValue(CLDevice device) { 251 return device.hasImageSupport() ? 1 : 0; 252 } 253 }, 254 /** 255 * Prefer devices that support out of order queues (see {@link CLDevice#hasOutOfOrderQueueSupport() }) 256 */ 257 OutOfOrderQueueSupport { 258 Comparable extractValue(CLDevice device) { 259 return device.hasOutOfOrderQueueSupport() ? 1 : 0; 260 } 261 }, 262 /** 263 * Prefer devices with the greatest variety of supported image formats (see {@link CLContext#getSupportedImageFormats(CLMem.Flags, CLMem.ObjectType) }) 264 */ 265 MostImageFormats { 266 Comparable extractValue(CLDevice device) { 267 if (!device.hasImageSupport()) 268 return 0; 269 // TODO: fix that ugly hack ? 270 CLContext context = JavaCL.createContext(null, device); 271 try { 272 return (Integer)context.getSupportedImageFormats(CLMem.Flags.ReadWrite, CLMem.ObjectType.Image2D).length; 273 } finally { 274 context.release(); 275 } 276 } 277 }; 278 279 Comparable extractValue(CLDevice device) { 280 throw new RuntimeException(); 281 } 282 } 283 284 public static class DeviceComparator implements Comparator<CLDevice> { 285 286 private final List<DeviceFeature> evals; 287 public DeviceComparator(List<DeviceFeature> evals) { 288 this.evals = evals; 289 } 290 291 @Override 292 public int compare(CLDevice a, CLDevice b) { 293 for (DeviceFeature eval : evals) { 294 if (eval == null) 295 continue; 296 297 Comparable va = eval.extractValue(a), vb = eval.extractValue(b); 298 int c = va.compareTo(vb); 299 if (c != 0) 300 return c; 301 } 302 return 0; 303 } 304 305 } 306 public static CLDevice getBestDevice(List<DeviceFeature> evals, Collection<CLDevice> devices) { 307 List<CLDevice> list = new ArrayList<CLDevice>(devices); 308 Collections.sort(list, new DeviceComparator(evals)); 309 return !list.isEmpty() ? list.get(list.size() - 1) : null; 310 } 311 312 public CLDevice getBestDevice() { 313 return getBestDevice(Arrays.asList(DeviceFeature.MaxComputeUnits), Arrays.asList(listAllDevices(true))); 314 } 315 316 /** Bit values for CL_CONTEXT_PROPERTIES */ 317 public enum ContextProperties implements com.nativelibs4java.util.ValuedEnum { 318 //D3D10Device(CL_CONTEXT_D3D10_DEVICE_KHR), 319 GLContext(CL_GL_CONTEXT_KHR), 320 EGLDisplay(CL_EGL_DISPLAY_KHR), 321 GLXDisplay(CL_GLX_DISPLAY_KHR), 322 WGLHDC(CL_WGL_HDC_KHR), 323 Platform(CL_CONTEXT_PLATFORM), 324 CGLShareGroupApple(CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE), 325 CGLShareGroup(CL_CGL_SHAREGROUP_KHR); 326 327 ContextProperties(long value) { this.value = value; } 328 long value; 329 @Override 330 public long value() { return value; } 331 332 public static long getValue(EnumSet<ContextProperties> set) { 333 return EnumValues.getValue(set); 334 } 335 336 public static EnumSet<ContextProperties> getEnumSet(long v) { 337 return EnumValues.getEnumSet(v, ContextProperties.class); 338 } 339 } 340 341 public CLContext createContextFromCurrentGL() { 342 return createGLCompatibleContext(listAllDevices(true)); 343 } 344 345 static Map<ContextProperties, Object> getGLContextProperties(CLPlatform platform) { 346 Map<ContextProperties, Object> out = new LinkedHashMap<ContextProperties, Object>(); 347 348 if (Platform.isMacOSX()) { 349 Pointer<?> context = OpenGLContextUtils.CGLGetCurrentContext(); 350 Pointer<?> shareGroup = OpenGLContextUtils.CGLGetShareGroup(context); 351 out.put(ContextProperties.CGLShareGroupApple, shareGroup.getPeer()); 352 } else if (Platform.isWindows()) { 353 Pointer<?> context = OpenGLContextUtils.wglGetCurrentContext(); 354 Pointer<?> dc = OpenGLContextUtils.wglGetCurrentDC(); 355 out.put(ContextProperties.GLContext, context.getPeer()); 356 out.put(ContextProperties.WGLHDC, dc.getPeer()); 357 out.put(ContextProperties.Platform, platform.getEntity()); 358 } else if (Platform.isUnix()) { 359 Pointer<?> context = OpenGLContextUtils.glXGetCurrentContext(); 360 Pointer<?> dc = OpenGLContextUtils.glXGetCurrentDisplay(); 361 out.put(ContextProperties.GLContext, context.getPeer()); 362 out.put(ContextProperties.GLXDisplay, dc.getPeer()); 363 out.put(ContextProperties.Platform, platform.getEntity()); 364 } else 365 throw new UnsupportedOperationException("Current GL context retrieval not implemented on this platform !"); 366 367 //out.put(ContextProperties.Platform, platform.getEntity().getPointer()); 368 369 return out; 370 } 371 /** 372 * Calls <a href="http://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clCreateContext.html">clCreateContext</a>.<br> 373 */ 374 @Deprecated 375 public CLContext createGLCompatibleContext(CLDevice... devices) { 376 for (CLDevice device : devices) { 377 if (!device.isGLSharingSupported()) 378 continue; 379 380 try { 381 return createContext(getGLContextProperties(this), device); 382 } catch (Throwable th) {} 383 } 384 throw new UnsupportedOperationException("Failed to create an OpenGL-sharing-enabled OpenCL context out of devices " + Arrays.asList(devices)); 385 } 386 387 /** 388 * Calls <a href="http://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clCreateContext.html">clCreateContext</a>.<br> 389 * Creates an OpenCL context formed of the provided devices.<br> 390 * It is generally not a good idea to create a context with more than one device, 391 * because much data is shared between all the devices in the same context. 392 * @param devices devices that are to form the new context 393 * @return new OpenCL context 394 */ 395 public CLContext createContext(Map<ContextProperties, Object> contextProperties, CLDevice... devices) { 396 int nDevs = devices.length; 397 if (nDevs == 0) { 398 throw new IllegalArgumentException("Cannot create a context with no associated device !"); 399 } 400 Pointer<SizeT> ids = allocateSizeTs(nDevs); 401 for (int i = 0; i < nDevs; i++) { 402 ids.setSizeTAtIndex(i, devices[i].getEntity()); 403 } 404 405 ReusablePointers ptrs = ReusablePointers.get(); 406 Pointer<Integer> pErr = ptrs.pErr; 407 408 long[] props = getContextProps(contextProperties); 409 Pointer<SizeT> propsRef = props == null ? null : pointerToSizeTs(props); 410 //System.out.println("ERROR CALLBACK " + Long.toHexString(errCb.getPeer())); 411 long context = CL.clCreateContext(getPeer(propsRef), nDevs, getPeer(ids), 0, 0, getPeer(pErr)); 412 error(pErr.getInt()); 413; 414 return new CLContext(this, ids, context); 415 } 416 417 /** 418 * Calls <a href="http://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clGetDeviceIDs.html">clGetDeviceIDs</a>.<br> 419 * List all the devices of the specified types, with only the ones declared as available if onlyAvailable is true. 420 */ 421 @SuppressWarnings("deprecation") 422 public CLDevice[] listDevices(CLDevice.Type type, boolean onlyAvailable) { 423 Pointer<Integer> pCount = allocateInt(); 424 error(CL.clGetDeviceIDs(getEntity(), type.value(), 0, 0, getPeer(pCount))); 425 426 int nDevs = pCount.getInt(); 427 if (nDevs <= 0) { 428 return new CLDevice[0]; 429 } 430 431 Pointer<SizeT> ids = allocateSizeTs(nDevs); 432 433 error(CL.clGetDeviceIDs(getEntity(), type.value(), nDevs, getPeer(ids), 0)); 434 return getDevices(ids, onlyAvailable); 435 } 436 437 /** 438 * OpenCL profile string. Returns the profile name supported by the implementation. The profile name returned can be one of the following strings: 439 * <ul> 440 * <li>FULL_PROFILE if the implementation supports the OpenCL specification (functionality defined as part of the core specification and does not require any extensions to be supported).</li> 441 * <li>EMBEDDED_PROFILE if the implementation supports the OpenCL embedded profile. The embedded profile is defined to be a subset for each version of OpenCL. The embedded profile for OpenCL 1.0 is described in section 10.</li> 442 * </ul> 443 */ 444 @InfoName("CL_PLATFORM_PROFILE") 445 public String getProfile() { 446 return infos.getString(getEntity(), CL_PLATFORM_PROFILE); 447 } 448 449 /** 450 OpenCL version string. Returns the OpenCL version supported by the implementation. This version string has the following format: 451 OpenCL<space><major_version.min or_version><space><platform- specific information> 452 Last Revision Date: 5/16/09 Page 30 453 The major_version.minor_version value returned will be 1.0. 454 */ 455 @InfoName("CL_PLATFORM_VERSION") 456 public String getVersion() { 457 return infos.getString(getEntity(), CL_PLATFORM_VERSION); 458 } 459 460 private double versionValue = Double.NaN; 461 private static final Pattern VERSION_PATTERN = Pattern.compile("OpenCL (\\d+\\.\\d+)\\b.*"); 462 double getVersionValue() { 463 if (Double.isNaN(versionValue)) { 464 String versionString = getVersion(); 465 Matcher matcher = VERSION_PATTERN.matcher(versionString); 466 if (matcher.matches()) { 467 String str = matcher.group(1); 468 versionValue = Double.parseDouble(str); 469 } else { 470 log(Level.SEVERE, "Failed to parse OpenCL version: '" + versionString + "'"); 471 } 472 } 473 return versionValue; 474 } 475 void requireMinVersionValue(String feature, double minValue) { 476 requireMinVersionValue(feature, minValue, Double.NaN); 477 } 478 private Set<String> featuresCheckedForVersion = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>()); 479 void requireMinVersionValue(String feature, double minValue, double deprecationValue) { 480 double value = getVersionValue(); 481 if (value < minValue) { 482 throw new CLVersionException(feature + " requires OpenCL version " + minValue + 483 " (detected version is " + value + ")"); 484 } else if (!Double.isNaN(deprecationValue) && featuresCheckedForVersion.add(feature)) { 485 Level level = null; 486 if (value < deprecationValue && JavaCL.verbose) 487 level = Level.INFO; 488 else if (value >= deprecationValue) 489 level = Level.WARNING; 490 if (level != null && shouldLog(level)) 491 log(level, feature + " is deprecated from OpenCL version " + deprecationValue + 492 " (detected version is " + value + ")"); 493 } 494 } 495 496 /** 497 * Platform name string. 498 */ 499 @InfoName("CL_PLATFORM_NAME") 500 public String getName() { 501 return infos.getString(getEntity(), CL_PLATFORM_NAME); 502 } 503 504 /** 505 * Platform vendor string. 506 */ 507 @InfoName("CL_PLATFORM_VENDOR") 508 public String getVendor() { 509 return infos.getString(getEntity(), CL_PLATFORM_VENDOR); 510 } 511 512 /** 513 * Returns a list of extension names <br> 514 * Extensions defined here must be supported by all devices associated with this platform. 515 */ 516 @InfoName("CL_PLATFORM_EXTENSIONS") 517 public String[] getExtensions() { 518 if (extensions == null) { 519 extensions = new LinkedHashSet<String>(Arrays.asList(infos.getString(getEntity(), CL_PLATFORM_EXTENSIONS).split("\\s+"))); 520 } 521 return extensions.toArray(new String[extensions.size()]); 522 } 523 private Set<String> extensions; 524 525 public boolean hasExtension(String name) { 526 getExtensions(); 527 return extensions.contains(name.trim()); 528 } 529 530 @InfoName("cl_nv_device_attribute_query") 531 public boolean isNVDeviceAttributeQuerySupported() { 532 return hasExtension("cl_nv_device_attribute_query"); 533 } 534 535 @InfoName("cl_nv_compiler_options") 536 public boolean isNVCompilerOptionsSupported() { 537 return hasExtension("cl_nv_compiler_options"); 538 } 539 540 @InfoName("cl_khr_byte_addressable_store") 541 public boolean isByteAddressableStoreSupported() { 542 return hasExtension("cl_khr_byte_addressable_store"); 543 } 544 545 @InfoName("cl_khr_gl_sharing") 546 public boolean isGLSharingSupported() { 547 return hasExtension("cl_khr_gl_sharing") || hasExtension("cl_APPLE_gl_sharing"); 548 } 549 550 /** 551 * Allows the implementation to release the resources allocated by the OpenCL compiler for this platform. 552 */ 553 public void unloadPlatformCompiler() { 554 if (getVersionValue() < 1.2) { 555 requireMinVersionValue("clUnloadCompiler", 1.1, 1.2); 556 error(CL.clUnloadCompiler()); 557 } else { 558 requireMinVersionValue("clUnloadPlatformCompiler", 1.2); 559 error(CL.clUnloadPlatformCompiler(getEntity())); 560 } 561 } 562 563}