//JFR Event Streaming with Java 14

The Java Flight Recorder has a long history. It used to be part of the BEA JRockit JVM, became a commercial feature of the Oracle JDK (7+) after Oracle acquired BEA and Sun and was finally fully open sourced with the release of OpenJDK 11 (backports exist for 8) (JEP328). OpenJDK 14 will add some improvements to the JFR.

JEP 349 will allow the continuous consumption of java flight recorder events in-memory from within the same JVM, or out-of-process from a different JVM via its JFR repository file.

JEP 349 made it already into the early access builds and can be experimented with since OpenJDK 14 build 22+. Lets check it out.

In-Process Streaming

The base JFR configuration files (XML) can be found in JDK_HOME/lib/jfr. The default configuration (default.jfc) is relatively low overhead, while profile.jfc will provide more data. Java Mission Control can create custom settings based on templates if needed. I am using the default config for the examples.

The first example will start JFR on the local JVM using the default recorder settings and register a few event handlers to check if it is working.

import java.io.IOException;
import java.text.ParseException;
import jdk.jfr.Configuration;
import jdk.jfr.consumer.EventStream;
import jdk.jfr.consumer.RecordingStream;

public class JFRStreamTest {
    
    public static void main(String[] args) throws IOException, ParseException  {
        
        Configuration config = Configuration.getConfiguration("default");
        System.out.println(config.getDescription());
        System.out.println("settings:");
        config.getSettings().forEach((key, value) -> System.out.println(key+": "+value));
        
        // open a stream and start local recording
        try (EventStream es = new RecordingStream(config)) {
            
            // register event handlers
            es.onEvent("jdk.GarbageCollection", System.out::println);
            es.onEvent("jdk.CPULoad", System.out::println);
            es.onEvent("jdk.JVMInformation", System.out::println);
            
            // start and block
            es.start();
        }
    
    }
    
}

The above example should print information about the running JVM once, CPU load periodically and GC events when they occur.

Out-of-Process Streaming

Simply start the flight recorder as usual via jcmd <PID> JFR.start or via the JVM flag -XX:+FlightRecorder at startup. The repository location will be stored in the jdk.jfr.repository system property as soon JFR is running (new in Java 14). It can also be set at startup via a comma separated list of flight recorder options: -XX:FlightRecorderOptions=repository=./blackbox

Update thanks to Erik from Oracle in the comments section: The repository location can be also set using jcmd <PID> JFR.configure repositorypath=<directory>. If you set it after a recording has started, new data will be written in the new location.

$ jcmd -l | grep netbeans
8492 org.netbeans.Main ... --branding nb

$ jcmd 8492 JFR.start name=streamtest
Started recording 1. No limit specified, using maxsize=250MB as default.
Use jcmd 8492 JFR.dump name=streamtest filename=FILEPATH to copy recording data to file.

$ jinfo -sysprops 8492 | grep jfr
jdk.jfr.repository=/tmp/2019_11_18_02_19_59_8492

Now that the recording is running and we know where the repository is, a second JVM can open a stream to the live JFR repository and monitor the application. Note that we did not dump any JFR records to a file, we connect to the live repository directly.

import java.io.IOException;
import java.nio.file.Path;
import jdk.jfr.consumer.EventStream;

public class JFRStreamTest {
    
    public static void main(String[] args) throws IOException  {
        // connect to JFR repository
        try (EventStream es = EventStream.openRepository(Path.of("/tmp/2019_11_18_02_19_59_8492"))) {
            
            // register some event handlers
            //es.onEvent("jdk.CPULoad", System.out::println);
            es.onEvent("jdk.SocketRead", System.out::println);
            es.onEvent("jdk.SocketWrite", System.out::println);
            
            // start and block
            es.start();
        }
    
    }
    
}

As a quick test i monitored with the example above a NetBeans instance running on Java 14 and let the IDE check for updates. Since we watch for SocketRead and Write events the output looked like:

jdk.SocketRead {
  startTime = 04:34:09.571
  duration = 117.764 ms
  host = "netbeans.apache.org"
  address = "40.79.78.1"
  port = 443
  timeout = 30.000 s
  bytesRead = 5 bytes
  endOfStream = false
  eventThread = "pool-5-thread-1" (javaThreadId = 163)
  stackTrace = [
    java.net.Socket$SocketInputStream.read(byte[], int, int) line: 68
    sun.security.ssl.SSLSocketInputRecord.read(InputStream, byte[], int, int) line: 457
    sun.security.ssl.SSLSocketInputRecord.decode(ByteBuffer[], int, int) line: 165
    sun.security.ssl.SSLTransport.decode(TransportContext, ByteBuffer[], int, int, ...
    sun.security.ssl.SSLSocketImpl.decode(ByteBuffer) line: 1460
    ...
  ]
}
...

Streaming Dumped Records

Opening streams (EventStream.openFile(path)) to JFR record dumps (jcmd <PID> JFR.dump filename=foo.jfr) is also possible of course - works as expected.

Conclusion

Pretty cool new feature! It is currently not possible to do in-memory but out-of-process steaming without syncing on repository files. But since a ramdisk can workaround this issue so easily I am not even sure if this capability would be worth it.

fly safe


//Running Debian Buster with 64bit mainline Kernel on a Raspberry Pi 3b+

The Raspberry Pi 3b+ runs on a Cortex A53 quad core processor which is using the Armv8 architecture. This allows it to run 64bit (AArch64) kernels, but be also backwards compatible with Armv7 and run 32bit (Armhf) kernels.

One of the most convenient ways to get started with a Raspberry Pi is to use the Debian based Raspbian distribution. Raspbian maintains its own customized linux kernel, drivers and firmware and provides 32bit OS images.

For my usecase however, I required the following:

  • get a server friendly 64bit OS running on the Pi3b+ (for example Debian)
  • use a mainline kernel
  • avoid proprietary binary blobs if possible
  • boot from a SSD instead of a SD card or USB stick (connected via USB adapter)

It turned out most of it was fairly easy to achieve thanks to the RaspberryPi3 debian port efforts. The raspi3-image-spec project on github contains instructions and the configuration for building a 64bit Debian Buster image which is using the mainline linux-image-arm64 kernels, ready to be booted from.

The image creation process is configured via raspi3.yaml which I modified to leave out wifi packages (i don't need wifi) and use netfilter tables (nft) as replacement for the now deprecated iptables but this could have been changed after initial boot too if desired.

The image will contain two partitions, the first is formatted as vfat labeled RASPIFIRM and contains the kernel, firmware and raspi specific boot configuration. The second ext4 partition is labeled RASPIROOT and contains the OS. Since partition labels are used the raspi should be able to find the partitions regardless of how they are connected or which drive type they are on (SSD, SD card reader, USB stick..).

Once the custom 64bit Debian image is built, simply write it to the SSD using dd, plug it in and power the raspi on. Once it booted don't forget to change the root password, create users, review ssh settings (disable root login), configure netfilter tables etc.

Some additional tweaks

Reduced the GPU memory RAM partition to 32MB to have more RAM left while still being able to use a terminal via a 1080p HDMI screen. Set cma=32M in /boot/firmware/cmdline.txt. I did also add a swapfile to free up additional memory by writing inactive pages to the SSD. The default image does not have swap configured.

Added
force_turbo=1

flag to /boot/firmware/config.txt. This will lock the ARM cores to their max clock frequency (1.4GHz). Since the linux kernel doesn't ship with a Pi 3 compatible cpufreq implementation yet, the Pi3b+ would just boot with idle clock (600Mhz) and never change it based on load. This is just a workaround until the CPU frequency scaling drivers made it into the kernel (update: commits made it into kernel 5.3). I didn't experience any CPU temperature issues so far, but i am also not running CPU intensive tasks.

The files will get overwritten on kernel updates, I added most of the /etc folder into a local git repository which makes diffs trivial and simplifies maintenance. If the raspi stops booting you can simply plug the SSD USB adapter into a workstation and edit the config files from there, diff, revert etc - very convenient.

USB Issues

I ran into an issue where the SSD connected via USB got disconnected after 5-30 days. This was very annoying to debug because it would not finish writing the kernel log file since virtually everything was on that disconnected drive. After swapping out everything hardware wise, compiling custom kernels (on the raspberry pi btw :) ), even falling back to the raspberry kernel which uses different USB drivers (dwc_otg instead of dwc2) I could not get rid of the disconnects - only delay it. Newer kernels seemed to improve the situation but what finally solved the issue for me was an old USB 2.0 hub i put in front of the SSD-USB adapter. Problem solved.


Thats about it. In case you are wondering what i am using the raspi for: This weblog you are reading right now is running on it (beside other things like a local wiki), for over six month by now, while only using 2-5W, fairly stable :)


//[Dynamic] [Application] Class Data Sharing in Java

Class Data Sharing as a JVM feature exists for quite some time already, but it became more popular in context of containers. CDS maps a pre-defined class archive into memory and makes it shareable between JVM processes. This can improve startup time but also reduce per-JVM footprint in some cases.

Although basic CDS was already supported in Java 5, this blog entry assumes that Java 11 or later is used, since JVM flags and overall capabilities changed over time. CDS might not be supported on all architectures or GCs: G1, CMS, ShenandoahGC, Serial GC, Parallel GC, and ParallelOld GC do all support CDS on 64bit linux, while ZGC for example doesn't have support for it yet.


JDK Class Data Sharing

The most basic form of CDS is setup to share only JDK class files. This is enabled by default since JDK 12 (JEP 341). If you check the java version using a shell you notice that the JVM version string will contain "sharing".
[mbien@hulk server]$ java -version
openjdk version "12.0.1" 2019-04-16
OpenJDK Runtime Environment AdoptOpenJDK (build 12.0.1+12)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 12.0.1+12, mixed mode, sharing)
The shared class archive can be found pre installed in ${JAVA_HOME}/lib/server/ called classes.jsa. By temporary renaming the file or switching to an older JDK, "sharing" won't appear anymore in the version string.
[mbien@hulk server]$ java -version
openjdk version "12.0.1" 2019-04-16
OpenJDK Runtime Environment AdoptOpenJDK (build 12.0.1+12)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 12.0.1+12, mixed mode)

The JDK CDS archive can be manually generated by invoking java -Xshare:dump with root privileges. Recent JVMs are configured to use -Xshare:auto at startup, which will automatically use CDS if available. Enforcing CDS with -Xshare:on will cause the JVM to fail if no archive is found.


Application Class Data Sharing

The basic CDS archive only contains JDK class data. Adding application class data (JEP 310) is fairly easy but comes with some limitations.

  • the classpath used at archive creation time must be the same as (or a prefix of) the classpath used at run time
  • wildcards or exploded JARs are not allowed in the path (no cheating)

1) create a list of classes (JDK and APP; Java 11+) to include in the shared archive:

$ java -Xshare:off -XX:DumpLoadedClassList=classes.list -jar foo.jar

2) create the archive using that list:

$ java -Xshare:dump -XX:SharedClassListFile=classes.list -XX:SharedArchiveFile=foo.jsa -cp foo.jar

3) launch the applications using the shared archive:

$ java -Xshare:on -XX:SharedArchiveFile=foo.jsa -jar foo.jar

-Xlog:class+load helps to verify that it is working properly.

[0.062s][info][class,load] java.lang.Object source: shared objects file
[0.062s][info][class,load] java.io.Serializable source: shared objects file
[0.062s][info][class,load] java.lang.Comparable source: shared objects file
[0.062s][info][class,load] java.lang.CharSequence source: shared objects file
...

The applications (containers) don't have to be identical to make use of custom CDS archives. The archive is based on the class list of step 1 which can be freely modified or merged with other lists. The main limitation is the classpath prefix rule.

Some Results

As quick test I used OpenJDK 11 + Eclipse Jetty + Apache JSP Wiki and edited a single page (to ensure the classes are loaded), generated a custom archive and started a few more instances with CDS enabled using the archive.

classes.list size = 4296 entries
wiki.jsa size = 57,6 MB 

 VIRT    RES     SHR  %MEM    TIME    COMMAND
2771292 315128  22448  8,3   0:20.54 java -Xmx128m -Xms128m -Xshare:off -cp ... 
2735468 259148  45184  6,9   0:18.38 java -Xmx128m -Xms128m -XX:SharedArchiveFile=wiki.jsa -cp ...
2735296 270044  45192  7,1   0:19.66 java -Xmx128m -Xms128m -XX:SharedArchiveFile=wiki.jsa -cp ...
2735468 259812  45060  6,9   0:18.26 java -Xmx128m -Xms128m -XX:SharedArchiveFile=wiki.jsa -cp ...
2735468 256800  45196  6,8   0:19.10 java -Xmx128m -Xms128m -XX:SharedArchiveFile=wiki.jsa -cp ...

The first JVM is started with CDS turned off as reference. The four other instances have sharing enabled using the custom archive.


Dynamic Class Data Archives

The proposal in JEP 350 which is currently targeted for Java 13 will allow to combine a static base archive with a dynamically generated archive.

The dynamic class data archive is created in a setup phase at application exit (-XX:ArchiveClassesAtExit=dynamic.jsa) and essentially automates step 1 and 2. Just like before -XX:SharedArchiveFile=dynamic.jsa will tell the JVM to map the shared archive. Since the dynamic archive references the static JDK archive, both will be used automatically.

The main advantage (beside the added convenience) is that the dynamic archive will support both builtin class loaders and user-defined class loaders.

now go forth and share class data :)


//Cleaning Bash History using a Java 11 Single-File Sourcecode Program

Java 11 adds with JEP330 the ability to launch a Java source file directly from the command line without requiring to explicitly compile it. I can see this feature being convenient for some simple scripting or quick tests. Although Java isn't the most concise language it still has the benefit of being quite readable and having powerful utility APIs.

The following example reads the bash shell history from a file into a LinkedHashSet and only keeps the last recently used line if duplicates exist, essentially cleaning up the history.


import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.LinkedHashSet;

public class HistoryCleaner {

    public static void main(String[] args) throws IOException {

        Path path = Path.of(System.getProperty("user.home")+"/.bash_history");

        LinkedHashSet<String> deduplicated = new LinkedHashSet<>(1024);
        Files.lines(path).forEachOrdered(line -> {
            deduplicated.remove(line);
            deduplicated.add(line);
        });

        Files.write(path, deduplicated);
    }

}

duke@virtual1:~$ java HistoryCleaner.java

will compile and run the "script".


Shebangs are supported too

#!/usr/bin/java --source 11

import java.io....
However, since this is not part of the java language specification, single-file java programs (SFJP) with a #! are technically no regular java files and will not compile when used with javac. This also means that the common file naming conventions are not required for pure SFJPs - using a different file extension is advised. We can't call it .js - so lets call it .sfjp :).
duke@virtual1:~$ ./HistoryCleaner.sfjp
Will compile and run the HistoryCleaner SFJP containing the shebang declaration in the first line. Keeping the .java file extension would result in a compiler error.

//NetBeans OpenCL Pack

Since I am doing a lot with OpenCL lately I decided to try to improve the tooling around OpenCL a bit. A weekend later the NetBeans OpenCL Pack was born :).

Features Including:

  • OpenCL Editor with syntax highlighting code completion and CL reference pages integration
  • OpenCL compiler integration
  • In-editor annotations of compiler warnings and errors updated as you type
  • JOCL project template

Technical Details:

The editor uses ANTLR as parser and lexer. This allows such simple things like keyword highlighting and also more complex features like semantic highlighting, formatting and auto completion (formatting is not yet implemented). It can also detect and report syntax errors, however this feature is automatically disabled if an OpenCL compiler is present on the host system. All with help of JOCL detected OpenCL implementations can be used as compiler backend.

Instead of using the old OpenGL Pack as template I decided to write it from scratch using latest NetBeans 7 and Java 7 APIs. So you will have to start NB with JDK7 to be able to use it.

Download

you can download it from the NetBeans plugin portal [mirror], sourcecode is on github

feedback and/or contributions/bugreports are as always appreciated

Screenshots:

auto completion editor project templates

have fun!


//Many little improvements made it into JOCL recently

Ok some of them are big, but I will only cover the little things with this blog entry :).

CLKernel

I added multiple utility methods to CLKernel and related classes. It is for example now possible to create a kernel and set its arguments in one line.

CLKernel sha512 = program.createCLKernel("sha512", padBuffer, digestBuffer, rangeBuffer);

Thanks to feedback in the jocl forums I also added methods to set vector typed arguments directly. In past you could do this only by setting them via a java.util.Buffer.

kernel.setArg(index, x, y, z, w);

Another small feature of CLKernel is to enforce 32bit arguments. You may want to switch between single and double floatingpoint precision at runtime or mix between both to improve performance you will have to compile the program with the double FP extension enabled. By setting kernel.setForce32bitArgs(true) all java doubles used as kernel arguments will be automatically cast down to 32bit CL floats (see MultiDeviceFractal demo for a example). This is nothing special but might safe you several if(single){setArg((float)foo)}else{setArg(foo)} constructs.

CLWork

CLKernel still only represents the function in the OpenCL program you want to call - nothing more. The new CLWork object contains everything required for kernel execution, like the NDRange and the kernel itself.

    int size = buffer.getNIOCapacity();
    CLWork1D work = CLWork.create1D(program.createCLKernel("sum", buffer, size));
    work.setWorkSize(size, 1).optimizeFor(device);

    // execute
    queue.putWriteBuffer(buffer, false)
         .putWork(work)
         .putReadBuffer(buffer, true);

optimizeFor(device) adjusts the workgroup size to meet device specific recommended values. This should make sure that all computing units of your GPU are used by dividing the work into groups (however this only works if your task does not care about the workgroup size, see javadoc).

CLSubDevice

Sometimes you don't want to put your CLDevice under 100% load. This might be the case for example if your device is the CPU your application is running on or if you have to share the GPU with an OpenGL context for rendering. One easy way of controlling device load is to limit the amount of compute units used for a task.

    CLPlatform platform = CLPlatform.getDefault(version(CL_1_1), type(CPU));

    CLDevice devices = platform.getMaxFLOPSDevice(type(CPU));
    CLSubDevice[] subs = device.createSubDevicesByCount(4, 4);
    // array contains now two virtual devices containing four CPU cores each

    CLContext context = CLContext.create(subs);
    CLCommandQueue queue = subs[0].createCommandQueue();
    ...

CLSubDevices extends CLDevice and can be used for context creation, queue creation and everywhere you would use the CLDevice. Prior to creating subdevices you should check if device.isFissionSupported() returns true.

CLProgram builder

Ok, this utility is not that new but I haven't blogged about it yet. If program.build() isn't enough you should take a look at the program builder. CLBuildConfiguration stores everything which is needed for program compilation and is easily configurable via the builder pattern :).

        // reusable builder
        CLBuildConfiguration builder = CLProgramBuilder.createConfiguration()
                                     .withOption(ENABLE_MAD)
                                     .forDevices(context.getDevices())
                                     .withDefine("RADIUS", 5)
                                     .withDefine("ENABLE_FOOBAR");
        builder.build(programA);
        builder.build(programB);
        ...

CLBuildConfiguration is fully reusable and can be upgraded to CLProgramConfiguration if you combine it with a CLProgram. Both can be serialised which allows to store the build configuration or the entire prebuild program on disc or send it over the network. (caching binaries on disc can safe startup time for example)

        // program configuration
        ois = new ObjectInputStream(new FileInputStream(file));
        CLProgramConfiguration programConfig = CLProgramBuilder.loadConfiguration(ois, context);
        assertNotNull(programConfig.getProgram());
        ois.close();
        program = programConfig.build(); // builds from source or loads binaries if possible
        assertTrue(program.isExecutable());

Note: loading binaries and associating them with the right driver/device is currently not trivial with OpenCL. Even if everything works as intended it is still possible that the driver refuses the binaries for some reason (driver update...etc). Thats why its recommended to add the program source to the configuration before calling build() to allow a automatic rebuild as fallback.

        // another entry point for complex builds (prepare() returns CLProgramConfiguration)
        program.prepare().withOption(ENABLE_MAD).forDevice(context.getMaxFlopsDevice()).build();

(all snippets have been stolen from the junit tests)
I am sure I forgot something... but this should cover at least some of the incremental improvements. Expect a few more blog entries for the larger features soon.

- - - - - -
In other news: Nvidia released OpenCL 1.1 drivers, some of us thought this would never happen -> all major vendors (AMD, Intel, NV, IBM, ZiiLABS ..) support now OpenCL 1.1 (screenshot)

have fun!


//Developing with JOCL on AMD, Intel and Nvidia OpenCL platforms

One nice feature of OpenCL is that the platform abstraction was handled in the spec from the first day on. You can install all OpenCL drivers side by side and let the application choose at runtime, on which device and on which platform it should execute the kernels.

As of today there are three four vendors which provide OpenCL implementations for the desktop. AMD and Intel support the OpenCL 1.1 specification where Nvidia apparently tries to stick with 1.0 to encourage their customers to stick with CUDA ;-). [edit] And of course there is also Apple providing out-of-the box OpenCL 1.0 support in MacOS 10.6.

JOCL contains a small CLInfo utility which can be used to quickly verify OpenCL installations. Here is the output on my system (ubuntu was booted) having all three SDKs installed:

CL_PLATFORM_NAMEATI StreamNVIDIA CUDAIntel(R) OpenCL
CL_PLATFORM_VERSIONOpenCL 1.1 ATI-Stream-v2.2 (302)OpenCL 1.0 CUDA 4.0.1OpenCL 1.1 LINUX
CL_PLATFORM_PROFILEFULL_PROFILEFULL_PROFILEFULL_PROFILE
CL_PLATFORM_VENDORAdvanced Micro Devices, Inc.NVIDIA CorporationIntel(R) Corporation
CL_PLATFORM_ICD_SUFFIX_KHRAMDNVIntel
CL_PLATFORM_EXTENSIONS[cl_khr_icd, cl_amd_event_callback][cl_khr_icd, cl_khr_byte_addressable_store, cl_nv_compiler_options, cl_nv_pragma_unroll, cl_nv_device_attribute_query, cl_khr_gl_sharing][cl_khr_icd, cl_khr_byte_addressable_store, cl_khr_fp64, cl_khr_local_int32_extended_atomics, cl_khr_local_int32_base_atomics, cl_khr_global_int32_base_atomics, cl_khr_gl_sharing, cl_intel_printf, cl_khr_global_int32_extended_atomics, cl_ext_device_fission]
CL_DEVICE_NAMEIntel(R) Core(TM) i7 CPU 940 @ 2.93GHzGeForce GTX 295GeForce GTX 295Intel(R) Core(TM) i7 CPU 940 @ 2.93GHz
CL_DEVICE_TYPECPUGPUGPUCPU
CL_DEVICE_AVAILABLEtruetruetruetrue
CL_DEVICE_VERSIONOpenCL 1.1 ATI-Stream-v2.2 (302)OpenCL 1.0 CUDAOpenCL 1.0 CUDAOpenCL 1.1
CL_DEVICE_PROFILEFULL_PROFILEFULL_PROFILEFULL_PROFILEFULL_PROFILE
CL_DEVICE_ENDIAN_LITTLEtruetruetruetrue
CL_DEVICE_VENDORGenuineIntelNVIDIA CorporationNVIDIA CorporationIntel(R) Corporation
CL_DEVICE_EXTENSIONS[cl_amd_device_attribute_query, cl_khr_byte_addressable_store, cl_khr_int64_extended_atomics, cl_khr_local_int32_extended_atomics, cl_amd_fp64, cl_amd_printf, cl_khr_local_int32_base_atomics, cl_khr_int64_base_atomics, cl_khr_global_int32_base_atomics, cl_khr_gl_sharing, cl_khr_global_int32_extended_atomics, cl_ext_device_fission][cl_khr_icd, cl_khr_byte_addressable_store, cl_khr_fp64, cl_khr_local_int32_extended_atomics, cl_khr_local_int32_base_atomics, cl_nv_compiler_options, cl_nv_pragma_unroll, cl_nv_device_attribute_query, cl_khr_global_int32_base_atomics, cl_khr_gl_sharing, cl_khr_global_int32_extended_atomics][cl_khr_icd, cl_khr_byte_addressable_store, cl_khr_fp64, cl_khr_local_int32_extended_atomics, cl_khr_local_int32_base_atomics, cl_nv_compiler_options, cl_nv_pragma_unroll, cl_nv_device_attribute_query, cl_khr_global_int32_base_atomics, cl_khr_gl_sharing, cl_khr_global_int32_extended_atomics][cl_khr_byte_addressable_store, cl_khr_fp64, cl_khr_local_int32_extended_atomics, cl_khr_local_int32_base_atomics, cl_khr_global_int32_base_atomics, cl_khr_gl_sharing, cl_intel_printf, cl_khr_global_int32_extended_atomics, cl_ext_device_fission]
CL_DEVICE_MAX_COMPUTE_UNITS830308
CL_DEVICE_MAX_CLOCK_FREQUENCY2934124212422930
CL_DEVICE_VENDOR_ID40984318431832902
CL_DEVICE_OPENCL_C_VERSIONOpenCL C 1.1 com.jogamp.opencl.CLException$CLInvalidValueException: error while asking for info string [error: CL_INVALID_VALUE]com.jogamp.opencl.CLException$CLInvalidValueException: error while asking for info string [error: CL_INVALID_VALUE]OpenCL C 1.1
CL_DRIVER_VERSION2.0270.41.06270.41.061.1
CL_DEVICE_ADDRESS_BITS64323264
CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT8118
CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR161116
CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT4114
CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG2112
CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT4114
CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE0112
CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR16com.jogamp.opencl.CLException$CLInvalidValueException: error while asking for info value [error: CL_INVALID_VALUE]com.jogamp.opencl.CLException$CLInvalidValueException: error while asking for info value [error: CL_INVALID_VALUE]16
CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT8com.jogamp.opencl.CLException$CLInvalidValueException: error while asking for info value [error: CL_INVALID_VALUE]com.jogamp.opencl.CLException$CLInvalidValueException: error while asking for info value [error: CL_INVALID_VALUE]8
CL_DEVICE_NATIVE_VECTOR_WIDTH_INT4com.jogamp.opencl.CLException$CLInvalidValueException: error while asking for info value [error: CL_INVALID_VALUE]com.jogamp.opencl.CLException$CLInvalidValueException: error while asking for info value [error: CL_INVALID_VALUE]4
CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG2com.jogamp.opencl.CLException$CLInvalidValueException: error while asking for info value [error: CL_INVALID_VALUE]com.jogamp.opencl.CLException$CLInvalidValueException: error while asking for info value [error: CL_INVALID_VALUE]2
CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF0com.jogamp.opencl.CLException$CLInvalidValueException: error while asking for info value [error: CL_INVALID_VALUE]com.jogamp.opencl.CLException$CLInvalidValueException: error while asking for info value [error: CL_INVALID_VALUE]0
CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT4com.jogamp.opencl.CLException$CLInvalidValueException: error while asking for info value [error: CL_INVALID_VALUE]com.jogamp.opencl.CLException$CLInvalidValueException: error while asking for info value [error: CL_INVALID_VALUE]4
CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE0com.jogamp.opencl.CLException$CLInvalidValueException: error while asking for info value [error: CL_INVALID_VALUE]com.jogamp.opencl.CLException$CLInvalidValueException: error while asking for info value [error: CL_INVALID_VALUE]2
CL_DEVICE_MAX_WORK_GROUP_SIZE10245125121024
CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS3333
CL_DEVICE_MAX_WORK_ITEM_SIZES[1024, 1024, 1024][512, 512, 64][512, 512, 64][1024, 1024, 1024]
CL_DEVICE_MAX_PARAMETER_SIZE4096435243521024
CL_DEVICE_MAX_MEM_ALLOC_SIZE10737418242348318722347008003154703360
CL_DEVICE_GLOBAL_MEM_SIZE322122547293932748893880320012618813440
CL_DEVICE_LOCAL_MEM_SIZE32768163841638432768
CL_DEVICE_HOST_UNIFIED_MEMORYtruecom.jogamp.opencl.CLException$CLInvalidValueException: error while asking for info value [error: CL_INVALID_VALUE]com.jogamp.opencl.CLException$CLInvalidValueException: error while asking for info value [error: CL_INVALID_VALUE]true
CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE655366553665536131072
CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE640064
CL_DEVICE_GLOBAL_MEM_CACHE_SIZE3276800262144
CL_DEVICE_MAX_CONSTANT_ARGS899128
CL_DEVICE_IMAGE_SUPPORTfalsetruetruetrue
CL_DEVICE_MAX_READ_IMAGE_ARGS0128128128
CL_DEVICE_MAX_WRITE_IMAGE_ARGS088128
CL_DEVICE_IMAGE2D_MAX_WIDTH0409640968192
CL_DEVICE_IMAGE2D_MAX_HEIGHT032768327688192
CL_DEVICE_IMAGE3D_MAX_WIDTH0204820482048
CL_DEVICE_IMAGE3D_MAX_HEIGHT0204820482048
CL_DEVICE_IMAGE3D_MAX_DEPTH0204820482048
CL_DEVICE_MAX_SAMPLERS01616128
CL_DEVICE_PROFILING_TIMER_RESOLUTION110001000340831
CL_DEVICE_EXECUTION_CAPABILITIES[EXEC_KERNEL, EXEC_NATIVE_KERNEL][EXEC_KERNEL][EXEC_KERNEL][EXEC_KERNEL, EXEC_NATIVE_KERNEL]
CL_DEVICE_HALF_FP_CONFIG[][][][]
CL_DEVICE_SINGLE_FP_CONFIG[DENORM, INF_NAN, ROUND_TO_NEAREST, ROUND_TO_INF, ROUND_TO_ZERO][INF_NAN, ROUND_TO_NEAREST, ROUND_TO_INF, ROUND_TO_ZERO, FMA][INF_NAN, ROUND_TO_NEAREST, ROUND_TO_INF, ROUND_TO_ZERO, FMA][DENORM, INF_NAN, ROUND_TO_NEAREST]
CL_DEVICE_DOUBLE_FP_CONFIG[][DENORM, INF_NAN, ROUND_TO_NEAREST, ROUND_TO_INF, ROUND_TO_ZERO, FMA][DENORM, INF_NAN, ROUND_TO_NEAREST, ROUND_TO_INF, ROUND_TO_ZERO, FMA][DENORM, INF_NAN, ROUND_TO_NEAREST, ROUND_TO_INF, ROUND_TO_ZERO, FMA]
CL_DEVICE_LOCAL_MEM_TYPEGLOBALLOCALLOCALGLOBAL
CL_DEVICE_GLOBAL_MEM_CACHE_TYPEREAD_WRITENONENONEREAD_WRITE
CL_DEVICE_QUEUE_PROPERTIES[PROFILING_MODE][OUT_OF_ORDER_MODE, PROFILING_MODE][OUT_OF_ORDER_MODE, PROFILING_MODE][OUT_OF_ORDER_MODE, PROFILING_MODE]
CL_DEVICE_COMPILER_AVAILABLEtruetruetruetrue
CL_DEVICE_ERROR_CORRECTION_SUPPORTfalsefalsefalsefalse
cl_khr_fp16falsefalsefalsefalse
cl_khr_fp64falsetruetruetrue
cl_khr_gl_sharing | cl_APPLE_gl_sharingtruetruetruetrue

The CLInfo utility is part of the jocl-demos project and is also available via webstart. For a plain text version of the above output you can run:

 java -jar jocl.jar:gluegen-rt.jar\
    -Djava.library.path="path/to/jocl/libs:path/to/gluegen/libs" com.jogamp.opencl.util.CLInfo

(btw to install the intel sdk on debian based systems follow this link)

happy coding!


//Java Binding for the OpenCL API

I am currently working on Java Binding for the OpenCL API using GlueGen (as used in JOGL, JOAL). The project started as part of my bachelor of CS thesis short after the release of the first OpenCL specification draft and is now fully feature complete with OpenCL 1.1. JOCL is currently in the stabilization phase, a beta release shouldn't be far away.

Overview - How does it work?

JOCL enables applications running on the JVM to use OpenCL for massively parallel, high performance computing tasks, executed on heterogeneous hardware (GPUs, CPUs, FPGAs etc) in a platform independent manner. JOCL consists of two parts, the low level and the high level binding.

The low level bindings (LLB) are automatically generated using the official OpenCL headers as input and provide a high performance, JNI based, 1:1 mapping to the C functions.

This has the following advantages:

  • reduces maintenance overhead and ensures spec conformance
  • compiletime JNI bindings are the fastest way to access native libs from the JVM
  • makes translating OpenCL C code into Java + JOCL very easy (e.g. from books or tutorials)
  • flexibility and stability: OpenCL libs are loaded dynamically and accessed via function pointers

The hand written high level bindings (HLB) is build on top of LLB and hides most boilerplate code (like object IDs, pointers and resource management) behind easy to use java objects. HLB use direct NIO buffers internally for fast memory transfers between the JVM and the OpenCL implementation and is very GC friendly. Most of the API is designed for method chaining but of course you don't have to use it this way if you don't want to. JOCL also seamlessly integrates with JOGL 2 (both are built and tested together). Just pass the JOGL context as parameter to the JOCL context factory and you will receive a shared context. If you already know OpenCL and Java, HLB should be very intuitive for you.

The project is available on jogamp.org. Please use the mailinglist / forum for feedback or questions and the bugtracker if you experience any issues. The JOCL root repository is located on github, you may also want to take a look at the jocl-demos project. (If the demos are not enough you might also want to take a look at the junit tests)

Screenshots (sourcecode in jocl-demos project):

JOCL Julia Set high precision

More regarding OpenGL interoperability and other features in upcoming blog entries.

The following sample shows basic setup, computation and cleanup using the high level APIs.

Hello World or parallel a+b=c

/**
 * Hello Java OpenCL example. Adds all elements of buffer A to buffer B
 * and stores the result in buffer C.
 * Sample was inspired by the Nvidia VectorAdd example written in C/C++
 * which is bundled in the Nvidia OpenCL SDK.
 * @author Michael Bien
 */
public class HelloJOCL {

    public static void main(String[] args) throws IOException {
        // Length of arrays to process (arbitrary number)
        int elementCount = 11444777;
        // Local work size dimensions
        int localWorkSize = 256;
        // rounded up to the nearest multiple of the localWorkSize
        int globalWorkSize = roundUp(localWorkSize, elementCount);

        // setup
        CLContext context = CLContext.create();

        CLProgram program = context.createProgram(
                       HelloJOCL.class.getResourceAsStream("VectorAdd.cl")
                                 ).build();

        CLBuffer<FloatBuffer> clBufferA =
                       context.createFloatBuffer(globalWorkSize, READ_ONLY);
        CLBuffer<FloatBuffer> clBufferB =
                       context.createFloatBuffer(globalWorkSize, READ_ONLY);
        CLBuffer<FloatBuffer> clBufferC =
                       context.createFloatBuffer(globalWorkSize, WRITE_ONLY);

        out.println("used device memory: "
            + (clBufferA.getSize()+clBufferB.getSize()+clBufferC.getSize())/1000000 +"MB");

        // fill read buffers with random numbers (just to have test data).
        fillBuffer(clBufferA.getBuffer(), 12345);
        fillBuffer(clBufferB.getBuffer(), 67890);

        // get a reference to the kernel functon with the name 'VectorAdd'
        // and map the buffers to its input parameters.
        CLKernel kernel = program.createCLKernel("VectorAdd");
        kernel.putArgs(clBufferA, clBufferB, clBufferC).putArg(elementCount);

        // create command queue on fastest device.
        CLCommandQueue queue = context.getMaxFlopsDevice().createCommandQueue();

        // asynchronous write to GPU device,
        // blocking read later to get the computed results back.
        long time = nanoTime();
        queue.putWriteBuffer(clBufferA, false)
             .putWriteBuffer(clBufferB, false)
             .put1DRangeKernel(kernel, 0, globalWorkSize, localWorkSize)
             .putReadBuffer(clBufferC, true);
        time = nanoTime() - time;

        // cleanup all resources associated with this context.
        context.release();

        // print first few elements of the resulting buffer to the console.
        out.println("a+b=c results snapshot: ");
        for(int i = 0; i < 10; i++)
            out.print(clBufferC.getBuffer().get() + ", ");
        out.println("...; " + clBufferC.getBuffer().remaining() + " more");

        out.println("computation took: "+(time/1000000)+"ms");

    }

    private static final void fillBuffer(FloatBuffer buffer, int seed) {
        Random rnd = new Random(seed);
        while(buffer.remaining() != 0)
            buffer.put(rnd.nextFloat()*100);
        buffer.rewind();
    }

    private static final int roundUp(int groupSize, int globalSize) {
        int r = globalSize % groupSize;
        if (r == 0) {
            return globalSize;
        } else {
            return globalSize + groupSize - r;
        }
    }

}

VectorAdd.cl

    // OpenCL Kernel Function for element by element vector addition
    kernel void VectorAdd(global const float* a,
                          global const float* b,
                          global float* c, int numElements) {

        // get index into global data array
        int iGID = get_global_id(0);

        // bound check (equivalent to the limit on a 'for' loop)
        if (iGID >= numElements)  {
            return;
        }

        // add the vector elements
        c[iGID] = a[iGID] + b[iGID];
    }

//New Getting Started with JOGL 2 tutorials

Thanks to Justin Stoecker, computer science graduate student at the University of Miami, JOGL gets a new set of getting started tutorials:

JOGL, or Java Bindings for OpenGL, allows Java programs to access the OpenGL API for graphics programming. The graphics code in JOGL programs will look almost identical to that found in C or C++ OpenGL programs, as the API is automatically generated from C header files. This is one of the greatest strengths of JOGL, as it is quite easy to port OpenGL programs written in C or C++ to JOGL; learning JOGL is essentially learning OpenGL[...]

Tutorials:

Thanks Justin!

//JOGL 2 - Composeable Pipline

JOGL provides a feature called 'composeable pipeline' which can be quite useful in some situations. It enables you to put additional delegating layers between your java application and the OpenGL driver. A few usecases could be:
  • performance metrics
  • logging, debugging or diagnostics
  • to ignore specific function calls
It is very easy to set up. Just put this line into your code and the DebugGL layer will throw a GLException as soon an error occurs (you want this usually when you are developing the software).
    public void init(GLAutoDrawable drawable) {
        // wrap composeable pipeline in a Debug utility, all OpenGL error codes are automatically
        // converted to GLExceptions as soon as they appear
        drawable.setGL(new DebugGL3(drawable.getGL().getGL3()));
        //..
    }
Another predefined layer is TraceGL which intercepts all OpenGL calls and prints them to an output stream.
        drawable.setGL(new TraceGL3(drawable.getGL().getGL3(), System.out));
see also GL Profiles

//Converting from CVS to GIT

This post is a short description how to convert a CVS repository into GIT. I used the tool cvs2svn from Tigris.org for this task. As an example I will use the project jake2.

0. Install cvs2svn and cvs

('cvs2svn' not only does cvs2svn conversion it also contains the shell commands cvs2git and cvs2bzr)
sudo apt-get install cvs
sudo apt-get install cvs2svn

1. Download the CVS repository

This is mandatory. In contrary to SVN you can't just read out the complete versioning history just by using the CVS protocol. To convert from CVS you will need direct file access to the repository.

Sourceforge for example supports repository synchronization via rsync to allow backups. Thats what I used to get a copy of the jake2 repository.

rsync -av rsync://jake2.cvs.sourceforge.net/cvsroot/jake2/* .

2. Convert into GIT fast-import format

Take a look at /usr/share/doc/cvs2svn/examples/cvs2git-example.options.gz and extract the example configuration file. The file is very good documented so I won't repeat things here. But the most important step is to provide an author mapping from cvs to git authors (search for 'author_transforms').
cvs2git --options=cvs2git.options

This will produce a dump and blob file for step 3.

3. Create GIT repository and import CVS history

mkdir gitrepo
cd gitrepo
git init
cat ../repo-tmp/git-blob.dat ../repo-tmp/git-dump.dat | git fast-import

4. Manual cleanup

cvs2git will do its best to convert tags and branches into git tags and branches and usually add an artificial commit do document that. Now you will probably want to take a look at the repository with a history browser like gitk or giggle... and clean it up a bit. E.g remove branches which could be tags etc. If this is to much work you might consider to tweak the configuration of step 2 and convert again with different strategies.

- - - -

btw you can find the jake2 git repo here.


//You have won the Jackpot 3.0

You probably remember the project called Jackpot which James Gosling was initially involved with. It was basically a way to migrate client code between incompatible third party libraries by specifying refactoring rules. The project was that good integrated into NetBeans that it looked dead from the outside for a long time, since it was only used internally. NetBeans 6.9 uses Jackpot for most of the in-code hints for instance.

There where various ways to specify the transformation rules, e.g. via a special declarative language or even in Annotations directly in the library-code which would cause incompatibilities (or e.g in conjunction with @Deprecated).

Jan Lahoda recently started with the efforts to make the project usable as standalone tool again. Jackpot 3.0 is available via bitbucket for early adopters.

Back to the Future

I used this opportunity to test jackpotc (the jackpot compiler) with JOGL. What I tired is to provide transformations which transform old JOGL 1.1.1 code into latest JOGL 2 compatible client code. So firstly thanks to Jan for fixing all the bugs we run into while testing the experimental commandline compiler.

The first thing I did was to transform the code to properly use OpenGL profiles. As testcode i will use the famous Gears OpenGL demo (but those kind of automatic transformations will only pay of if you use them on large codebases). Since it was written against JOGL 1.1.1 it can only use OpenGL up to version 2.x, which means we can simply use the GL2 profile.

Transformation Rules

'JOGL2 API change: javax.media.opengl.GL -> javax.media.opengl.GL2':
javax.media.opengl.GL=>javax.media.opengl.GL2;;

'JOGL2 API change: new javax.media.opengl.GLCapabilities(javax.media.opengl.GLProfile)':
new javax.media.opengl.GLCapabilities()=>
new javax.media.opengl.GLCapabilities(javax.media.opengl.GLProfile.get(javax.media.opengl.GLProfile.GL2));;

'JOGL2 API change: GL gl = drawable.getGL() -> GL2 gl = drawable.getGL().getGL2()':
$d.getGL() :: $d instanceof javax.media.opengl.GLAutoDrawable=>
$d.getGL().getGL2();; 

Just by looking at the transformation rules you can easily see that it is far more powerfull as any simple text replacement could be. Jackpot uses javac and can therefore work with full qualified names, instanceof and more. It will also correctly fix imports for you (there is currently a open bug in this area). The quotes are used as description string which will be printed when jackpotc runs on every code occurrence which applies.

Invoking Jackpot

jackpotc -sourcepath $SRC -cp $LIBS -d $OUTPUT\
         -Ajackpot30_extra_hints=./jogl1Tojogl2.hint $FILESET

$LIBS must contain both library versions, JOGL 1.1.1 and JOGL 2. This is not optimal but it will probably work in most situations to just use both without thinking about an particular ordering or the need to do multiple iterations.

Results

If everything runs fine the console output should look like the sample below for each transformation which applies for the given $FILESET:
./test/oldgears/src/jogl111/gears/Gears.java:54: warning: JOGL2 API change: GL gl = drawable.getGL() -> GL2 gl = drawable.getGL().getGL2()
    GL gl = drawable.getGL();
...
The final result is a diff patch located in $OUTPUT/META_INF/upgrade called upgrade.diff containing the complete changeset for the transformation. Now the only thing you have to do is to review the changes and apply them.
@@ -51,7 +51,7 @@
     // Use debug pipeline
     // drawable.setGL(new DebugGL(drawable.getGL()));
 
-    GL gl = drawable.getGL();
+    GL2 gl = drawable.getGL().getGL2();
...

You can find the complete demo and all ready-to-run shellscripts in the tools/jackpotc folder inside JOGL's git repository. The classic JOGL 2 Gears demo can be found in form of an applet here (uses latest hudson builds... can be unstable).

happy coding!


- - - -
The JOGL repositories are open for contributions. If you would like to add some rules or fix other things... feel free to fork the repos on github and commit to them. (same rule applies for all JogAmp Projects like JOCL, JOAL, GlueGen... etc)

//JogAmp at SIGGRAPH 2010

The JogAmp team will be present at SIGGRAPH this year:
3D & Multimedia Across Platforms and Devices Using JOGL
Tuesday, 27 July | 4:00 PM - 6:00 PM

This session discusses the features, contributions, and future of OpenGL, OpenCL, and OpenMax
across devices and OS exposed on top of Java using the JogAmp open-source libraries.
link to Session

hope to meet you there.

about JogAmp.
JogAmp is the home of high performance Java libraries for 3D Graphics, Multimedia and Processing. JogAmp consists currently of the projects JOGL, JOCL and JOAL which provide cross platform language bindings to the OpenGL, OpenCL, OpenAL and OpenMAX APIs.


- - - -
(yes i know i should start bogging again :))


//NetBeans GIT support

If you are using GIT as SCM and NetBeans as IDE you should probably check out NBGit. The plugin integrates GIT in NetBeans in the same way as the out of the box Mercurial support does it. In fact both modules have the same origin since nbgit is a fork of the mercurial integration project and incrementally adds features to catch up.

NBGit Version 0.3 is already fairly stable and provides the basic set of features you would expect from distributed versioning system IDE integration.

Features

  • Graph visualization of parallel branches (Browser similar to giggle)
  • Versioning History (git log)
  • Show changes (git status)
  • update/commit/reset
  • clone/clone other/git init
  • custom actions (custom git commands)
  • diff
  • in-editor annotation of code changes
  • ignore files (parsing '.gitignore' files)
  • git properties (username, email etc via options)

The project is developed by volunteers outside Sun, if you like to see GIT integration as out-of-the-box feature in a future version of NetBeans please vote for this RFE.

I use the plugin for most of my open source projects and haven't experience any serious issues so far. I would say its already safe to use since you can't do anything wrong if you do a 'git status' -> 'git push' via command line as last step anyway.


//JOGL 2 - OpenGL Profiles explained

June 16 2010, updated blogpost: OpenGL 4

JOGL 2 supports several OpenGL Profiles. In this blog entry I try to explain what profiles are and why they are needed.

History

SGI released the first OpenGL specification 1992. Since this point OpenGL 1.x constantly evolved (under the ARB and later Khronos Group) by adding new functions to the core API. This went well until programmable graphics hardware became mainstream and shaders became suddenly more flexible and efficient as the generic fixed function pipeline.

OpenGL 2.x was the last version in which you could freely mix the fixed function pipeline with the programmable pipeline (as a core feature).

With the release of OpenGL 3.0 the whole fixed function pipeline has been deprecated but you could still use it if you haven't requested a forward compatible context.

OpenGL 3.1 and 3.2 removed most deprecated functionality from core specification, however some implementations (e.g. Nvidia drivers) still allow to get them back via an optional compatibility extension. Since 3.1 was the first release which broke compatibility, it is often seen as major OpenGL 3 release.

JOGL 2 (JSR 231)

JOGL 1.1.1 lived in the timeframe up to OpenGL 3.0 which made it easy to stay in sync with the spec. To be able to solve the issue with the deprecation of functionality, JOGL 2 (JSR maintenance release) introduces an abstraction of the original OpenGL versioning called Profile. Profiles allow Java applications to be written in a way which allows compatibility with multiple OpenGL versions at the same time. Since OpenGL ES (GL for embedded systems) has overlapping functionality with OpenGL itself it opened the opportunity to add even Profiles which bridge desktop and embedded implementations. The class diagram below shows the dependencies between all available Profiles.

Before you start writing a JOGL application you will have to decide first which GLProfile you want to use. The code snippet below lists all currently supported profiles (extracted from GLProfile).


Current list of supported profiles and their mapping to the implementation versions

    /** The desktop OpenGL compatibility profile 4.x, with x >= 0, ie GL2 plus GL4.
bc stands for backward compatibility. */ public static final String GL4bc = "GL4bc"; /** The desktop OpenGL core profile 4.x, with x >= 0 */ public static final String GL4 = "GL4"; /** The desktop OpenGL compatibility profile 3.x, with x >= 1, ie GL2 plus GL3.
bc stands for backward compatibility. */ public static final String GL3bc = "GL3bc"; /** The desktop OpenGL core profile 3.x, with x >= 1 */ public static final String GL3 = "GL3"; /** The desktop OpenGL profile 1.x up to 3.0 */ public static final String GL2 = "GL2"; /** The embedded OpenGL profile ES 1.x, with x >= 0 */ public static final String GLES1 = "GLES1"; /** The embedded OpenGL profile ES 2.x, with x >= 0 */ public static final String GLES2 = "GLES2"; /** The intersection of the desktop GL2 and embedded ES1 profile */ public static final String GL2ES1 = "GL2ES1"; /** The intersection of the desktop GL3, GL2 and embedded ES2 profile */ public static final String GL2ES2 = "GL2ES2"; /** The intersection of the desktop GL3 and GL2 profile */ public static final String GL2GL3 = "GL2GL3";

Note: GL2 Profile supports OpenGL up to version 3.0 (included) - this is not a bug: OpenGL 3.1 was the big game changer

The next two code snippets show the basic steps how to set up OpenGL with JOGL 2.

Context creation

        //create a profile, in this case OpenGL 3.1 or later
        GLProfile profile = GLProfile.get(GLProfile.GL3);
        
        //configure context
        GLCapabilities capabilities = new GLCapabilities(profile);
        capabilities.setNumSamples(2); // enable anti aliasing - just as a example
        capabilities.setSampleBuffers(true);
        
        //initialize a GLDrawable of your choice
        GLCanvas canvas = new GLCanvas(capabilities);

        //register GLEventListener
        canvas.addGLEventListener(...);
        //... (start rendering thread -> start rendering...)

Rendering

    public void display(GLAutoDrawable drawable) {
        GL3 gl = drawable.getGL().getGL3();
        gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
        //.. render something
    }

Summary

Profiles make JOGL 2 very flexible and allow it to build modular and portable applications. For instance part A of an application can be written against the GL2ES2 interface and part B (which is more hardware specific) against the GL3 interface. This would in theory allow to reuse A in an embedded application and B could e.g. disable itself on old desktop hardware which runs only OpenGL 2.x or fall back to a GL2 implementation.

More information can be found on JogAmp.org (direct link to javadoc)

The next release of the OpenGL Pack for NetBeans will fully support JOGL 2. Beta builds can be found here (builds contain JOGL2 beta5):