Jogl - User's Guide

Overview

Jogl is a Java programming language binding for the OpenGL 3D graphics API. It supports integration with the Java platform's AWT and Swing widget sets while providing a minimal and easy-to-use API that handles many of the issues associated with building multithreaded OpenGL applications. Jogl provides access to the latest OpenGL routines (OpenGL 1.4 with vendor extensions) as well as platform-independent access to hardware-accelerated offscreen rendering ("pbuffers"). Jogl also provides some of the most popular features introduced by other Java bindings for OpenGL like GL4Java, LWJGL and Magician, including a composable pipeline model which can provide faster debugging for Java-based OpenGL applications than the analogous C program.

Jogl was designed for the most recent version of the Java platform and for this reason supports only J2SE 1.4 and later. It also only supports truecolor (15 bits per pixel and higher) rendering; it does not support color-indexed modes. Certain areas of the public APIs are more restrictive than in other bindings; for example, the GLCanvas and GLJPanel classes are final, unlike in GL4Java, and the GLContext class is no longer exposed in the public API. These changes have been made to keep the public API simple and because most of the programming errors that have been seen with earlier Java/OpenGL interfaces, in particular GL4Java, have been related to subclassing the OpenGL widget classes and performing manual OpenGL context management. Several complex and leading-edge OpenGL demonstrations have been successfully ported from C/C++ to Jogl without needing direct access to any of these APIs. However, all of these classes and concepts are accessible at the Java programming language level in implementation packages, and in fact the Jogl binding is itself written almost completely in the Java programming language. There are only about fifty lines of handwritten C code in the entire Jogl source base; the rest of the native code is autogenerated during the build process by a new tool called GlueGen, the source code of which is in the Jogl source tree. Documentation for GlueGen is forthcoming.

Creating a GLDrawable

Jogl provides two basic widgets into which OpenGL rendering can be performed. The GLCanvas is a heavyweight AWT widget which supports hardware acceleration and which is intended to be the primary widget used by applications. The GLJPanel is a fully Swing-compatible lightweight widget which currently does not support hardware acceleration but which is intended to provide 100% correct Swing integration in the rare circumstances where a GLCanvas can not be used. See this article on The Swing Connection for more information about mixing lightweight and heavyweight widgets.

Both the GLCanvas and GLJPanel implement a common interface called GLDrawable so applications can switch between them with minimal code changes. The GLDrawable interface provides

GLCanvas and GLJPanel instances are created using the factory methods in GLDrawableFactory. These factory methods allow the user to request a certain set of OpenGL parameters in the form of a GLCapabilities object as well as optionally customize the format selection algorithm by specifying a GLCapabilitiesChooser.

A GLCapabilities object specifies the OpenGL parameters for a newly-created widget, such as the color, alpha,, z-buffer and accumulation buffer bit depths and whether the widget is double-buffered. The default capabilities are loosely specified but provide for truecolor RGB, a reasonably large depth buffer, double-buffered, with no alpha, stencil, or accumulation buffers.

An application can override the default pixel format selection algorithm by providing a GLCapabilitiesChooser to the GLDrawableFactory. The chooseCapabilities method will be called with all of the available pixel formats as an array of GLCapabilities objects; it should return an integer index into this array. The DefaultGLCapabilitiesChooser attempts to provide a better cross-platform selection algorithm than the WGL and GLX pixel format selection algorithms.

Writing a GLEventListener

Applications implement the GLEventListener interface to perform OpenGL drawing. When the methods of the GLEventListener are called, the underlying OpenGL context associated with the drawable is already current. The listener fetches the GL object out of the GLDrawable and begins to perform rendering.

The init() method is called once, upon context creation. (Hooks for context destruction, and support for context recreation, are not yet implemented.) The display() method is called to perform per-frame rendering. The reshape() method is called when the drawable has been resized; the default implementation automatically resizes the OpenGL viewport so often it is not necessary to do any work in this method. The displayChanged() method is designed to allow applications to support on-the-fly screen mode switching, but support for this is not yet implemented so the body of this method should remain empty.

It is strongly recommended that applications always refetch the GL and GLU objects out of the GLDrawable upon each call to the init(), display() and reshape() methods and pass the GL object down on the stack to any drawing routines, as opposed to storing the GL in a field and referencing it from there. The reason is that multithreading issues inherent to the AWT toolkit make it difficult to reason about which threads certain operations are occurring on, and if the GL object is stored in a field it is unfortunately too easy to accidentally make OpenGL calls from a thread that does not have a current context. This will usually cause the application to crash. For more information please see the section on multithreading.

Using the Composable Pipeline

Jogl supports the "composable pipeline" paradigm introduced by the Magician Java binding for OpenGL. The DebugGL pipeline calls glGetError after each OpenGL call, reporting any errors found. It can greatly speed up development time because of its fine-grained error checking as opposed to the manual error checking usually required in OpenGL programs written in C. The TraceGL prints logging information upon each OpenGL call and is helpful when an application crash makes it difficult to see where the error occurred.

To use these pipelines, call GLDrawable.setGL at the beginning of the init method in your GLEventListener. For example,

class MyListener implements GLEventListener {
  public void init(GLDrawable drawable) {
    drawable.setGL(new DebugGL(drawable.getGL()));
    // ...
  }

  // ...
}

Multithreading Issues

Jogl was designed to interoperate with the AWT, an inherently multithreaded GUI toolkit. OpenGL, in contrast, was originally designed in single-threaded C programming environments. For this reason Jogl provides a framework in which it is possible to write correct multithreaded OpenGL applications using the GLEventListener paradigm.

If an application written using Jogl interacts in any way with the mouse or keyboard, the AWT is processing these events and the multithreaded aspects of the program must be considered.

OpenGL applications usually behave in one of two ways: either they repaint only on demand, for example when mouse input comes in, or they repaint continually, regardless of whether user input is coming in. In the repaint-on-demand model, the application can merely call GLDrawable.display() manually at the end of the mouse or keyboard listener to cause repainting to be done. Alternatively if the application knows the concrete type of the GLDrawable it can call repaint() to have the painting scheduled for a later time.

In the continuous repaint model, the application typically has a main loop which is calling GLDrawable.display() repeatedly, or is using the Animator class, which does this internally. In both of these cases the OpenGL rendering will be done on this thread rather than the internal AWT event queue thread which dispatches mouse and keyboard events.

Both of these models (repaint-on-demand and repaint continually) still require the user to think about which thread keyboard and mouse events are coming in on, and which thread is performing the OpenGL rendering. OpenGL rendering may not occur directly inside the mouse or keyboard handlers, because the OpenGL context for the drawable is not current at this point (hence the warning about storing a GL object in a field, where it can be fetched and accidentally used by another thread). However, a mouse or keyboard listener may invoke GLDrawable.display().

It is generally recommended that applications perform as little work as possible inside their mouse and keyboard handlers to keep the GUI responsive. However, since OpenGL commands can not be run from directly within the mouse or keyboard event listener, the best practice is to store off state when the listener is entered and retrieve this state during the next call to GLEventListener.display().

Furthermore, it is recommended that if there are long computational sequences in the GLEventListener's display method which reference variables which may be being simultaneously modified by the AWT thread (mouse and keyboard listeners) that copies of these variables be made upon entry to display and these copies be referenced throughout display() and the methods it calls. This will prevent the values from changing while the OpenGL rendering is being performed. Errors of this kind show up in many ways, including certain kinds of flickering of the rendered image as certain pieces of objects are rendered in one place and other pieces are rendered elsewhere in the scene. Restructuring the display() method as described has solved all instances of this kind of error that have been seen with Jogl to date.

In addition to correctness issues, there are also performance issues to consider with multithreaded OpenGL applications. The OpenGL context associated with a particular drawable can only be current on one thread at a time. If multiple threads may be making the context current then this implies that the context must be made current and freed during each render; the overhead of these context operations may be significant depending on the application. For this reason Jogl has a built-in mechanism for optimizing the OpenGL context handling to the efficiency of an analogous C application.

GLDrawable.setRenderingThread informs the Jogl library that rendering to a particular drawable will only occur from the specified thread. The intent is that the OpenGL context can be made current and remain current on that thread until setRenderingThread(null) is called. Unfortunately, due to quality-of-implementation bugs in the X11 JAWT, this optimization had to be made advisory; in other words, it was not possible to guarantee that setRenderingThread would yield any faster OpenGL context handling on these platforms.

In some situations, typically when an application is using pbuffers to compute intermediate results, it is required that automatic redraws be suspended for a particular drawable so that the application can completely control when and where the display() method is called. For this reason the GLDrawable.setNoAutoRedrawMode() method was added; it is used not only by the Jogl implementation but also by utility libraries such as gleem (included in the jogl-demos distribution). We consider it unfortunate that it was necessary to expose two APIs to express basically the same idea and hope that if the JAWT implementation in the 1.5 platform has better locking behavior that GLDrawable.setNoAutoRedrawMode() may be able to be removed.

Pbuffers

Jogl exposes hardware-accelerated offscreen rendering (pbuffers) with a minimal and platform-agnostic API. Several recent demos have been successfully ported from C/C++ to Java using Jogl's pbuffer APIs. However, the pbuffer support in Jogl remains one of the more experimental aspects of the package and the APIs may need to change in the future.

To create a pbuffer, create a GLCanvas and (assuming it reports that it can create an offscreen drawable) make a pbuffer using the createOffscreenDrawable API. Because of the multithreaded nature of the AWT, the pbuffer is actually created lazily. For this reason the application's main loop typically needs to detect when the init() methods of all of the GLEventListeners for all of the offscreen surfaces have been called. See the demonstrations such as the ProceduralTexturePhysics demo for an example of this.

Additionally, pbuffers are only created when the parent GLCanvas's display(), init(), or reshape() methods are called; in other words, it may be necessary to manually "prime" the GLCanvas by calling display() on it until it creates all of its requested pbuffers. Again, please see the demonstrations for concrete examples of this. We hope that it may be possible to hide many of these details in the future.

A pbuffer is used by calling its display() method. Rendering, as always, occurs while the pbuffer's OpenGL context is current. There are render-to-texture options that can be specified in the GLCapabilities for the pbuffer which can make it easier to operate upon the resulting pixels. These APIs are however extremely experimental and not yet implemented on all platforms.

Platform Notes

All Platforms

The following issues, among others, are outstanding on all platforms:

Windows

No outstanding issues at this time.

Solaris, Linux (X11 platforms)

Mac OS X

The Mac OS X port of Jogl, in particular the GL interface and its implementation, can be used either with the provided GLCanvas widget or with the Cocoa NSOpenGLView. In order to use it with Cocoa the following steps should be taken:

NOTE: the Cocoa interoperability has not yet been retested since the GLCanvas was implemented. Please report any problems found with using Jogl with an NSOpenGLView.

The following issues remain with the Mac OS X port: