summaryrefslogtreecommitdiffstats
path: root/src/com/jogamp/opencl/CLPlatform.java
blob: f64df614b5eab74f3015b79894257a86dc038935 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
/*
 * 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:
 * <p><pre>
 *     if( !CLPlatform.isAvailable() ) {
 *        return; // abort
 *     }
 *     try{
 *          CLPlatform.initialize();
 *     }catch(JogampRuntimeException ex) {
 *          throw new RuntimeException("could not load Java OpenCL Binding");
 *     }
 * </pre></p>
 *
 * Example initialization:
 * <p><pre>
 *     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();
 *     }
 * </pre></p>
 * concurrency:<br/>
 * 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<String> 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<CLPlatform>... 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<CLPlatform>[])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<CLPlatform>... 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<CLPlatform> platforms = new ArrayList<CLPlatform>();

        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() {
        try{
            return this.listCLDevices(CLDevice.Type.ALL);
        }
        catch(CLInvalidDeviceTypeException ignore){ //trying to list GPUs if CL_DEVICE_TYPE_ALL isn't valid. on some non-standard implementations (Android PowerVR), only CL_DEVICE_TYPE_GPU is supported and use of other types including ALL will lead to a CL_INVALID_DEVICE_TYPE
            return this.listCLDevices(CLDevice.Type.GPU);
        }
    }

    /**
     * Lists all physical devices available on this platform matching the given {@link CLDevice.Type}.
     */
    public CLDevice[] listCLDevices(final CLDevice.Type... types) {
        initialize();

        final List<CLDevice> list = new ArrayList<CLDevice>();

        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<CLDevice>... filters) {
        initialize();

        final List<CLDevice> list = new ArrayList<CLDevice>();

        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 <I> void addIfAccepted(final I item, final List<I> list, final Filter<I>[] filters) {
        if(filters == null) {
            list.add(item);
        }else{
            boolean accepted = true;
            for (final Filter<I> 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<CLDevice>... 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<String> getExtensions() {

        if(extensions == null) {
            extensions = new HashSet<String>();
            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<String, String> 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;
    }

}