OpenGL in Java: how to use hardware acceleration | by Mario Emmanuel | Towards Data Science

Hardware acceleration is often seen as a niche solution for game development, but there are many other graphical applications that can benefit from this technology, especially those involving data visualisation such as specialised data science tools or software displaying real time data. Conventional charting tools and libraries are not fast enough for this task and hence custom graphics programming is needed.
Hardware acceleration was once a high-end feature of specialised computers, such as Silicon Graphics UNIX workstations that were widely used in the film and TV industry during the 90s, but now it is a common feature present in every computer built during the last 15 or 20 years. While some graphics cards present more power and functionalities than others, a basic set of 2D and 3D acceleration is present in almost every modern device.
This is achieved by means of a specialised CPU (which is called GPU — graphics processing unit — ). A GPU is a specific purpose CPU which provides the floating-point and matrices processing capabilities which are commonly used in 2D and 3D rendering. It basically allows the software to draw complex graphics dealing with user coordinates, scaling, panning, zooming, rotations and texture rendering, both in 2D and 3D.
By providing such functionality close to the actual rastering output device, there is a reduction on both CPU usage and I/O bus bandwidth, which effectively enables to do complex renderings and animation, both in 2D and 3D. Relying on the main CPU for such tasks would imply that only less complex renderings could be done and that a lot of CPU time would be wasted to cope with the graphic routines.
In this post, I will briefly cover OpenGL and its usage in Java, including an introductory but completely useful tutorial. The tutorial covers 2D, but it is actually a good introduction to OpenGL in general, so if you are interested in 3D, it is still good introductory material, as concepts are really similar for both 2D and 3D.
There are two major competing standards in 2D and 3D acceleration: DirectX and OpenGL. It is important to understand that OpenGL and DirectX are hardware acceleration standards which define which APIs shall be exposed by graphics cards. In that sense, it is exactly the same as any other computing standard such as POSIX or the C++11 standard. Beyond the standards, there will be libraries implementing them, for DirectX libraries are provided directly by Microsoft and in the case of OpenGL, there are many open source solutions.
Major graphics cards manufacturers decide if their hardware devices will support one technology or the other or both (which is the usual case).
DirectX and OpenGL are not the only standards/APIs that provide 2D and 3D rendering capabilities. There are others, being the main ones Vulkan and Metal. However, both DirectX and OpenGL are basically the most widely used ones. Metal is focused on Apple devices and Vulkan intends to be an improvement over OpenGL. I have not evaluated those ones as I do not consider them interesting for my needs.
DirectX (Direct2D and Direct3D) is a set of APIs created by Microsoft which are implemented in Windows. DirectX does not only cover graphics but also audio and input. While they are standards and could be theoretically implemented in any OS architecture, the reality is that DirectX is used only for Microsoft Windows (and XBOX, in case someone finds that information relevant). So basically DirectX is the standard solution for the Microsoft ecosystem.
OpenGL, on the contrary, is intended to be a cross-platform language which can be used in every major OS released. It is actually the direct descendant of the old Silicon Graphics platforms which as early as 1991 defined IrisGL as a hardware API providing professional 2D and 3D rendering capabilities.
Almost every modern graphics card (and by modern I mean latest 15 years) will provide compatibility with a given release of both DirectX and/or OpenGL, so its usage is no longer an oddity and chances are that you are no longer operating any computer without hardware acceleration capabilities.
It is important to understand that these APIs are intended to provide rendering capabilities and not user interfaces. Some widgets/frameworks have been built on top of OpenGL and Direct2D to fulfil this need, although they are not as widely used as the regular frameworks operating without explicit hardware acceleration usage. The reason behind this is that hardware acceleration is usually linked to game development (which requires no standard user interfaces) and specialised graphical applications.
I have evaluated a few combinations regarding hardware acceleration. Main ones have been Direct2D and C++ and OpenGL with either C++ or Java. In my opinion, there is no clear answer about which combination is better.
If you do not need cross-platform (so you are basically developing for Windows) and you do not need to develop complex user interfaces either, I would say that Direct2D is the way to go. It is easier, it is well supported in Windows and it will fulfil all needs.
However, if you are interested in covering other OSs then OpenGL is definitively the way to go. Out of the box, it will support all major OSs.
Regarding using Java or C++, I personally chose Java because it allows easier integration with existing user interface frameworks (specifically, Java Swing). My needs involve both graphics acceleration and user interface, so my approach to OpenGL might be slightly different to most of the material you can find on Internet, which is usually focused on games development. This explains why it is so relevant to me how each language and user interface framework interacts with OpenGL.
For C++ under Windows and Direct2D, there is no straightforward answer on how to implement user interfaces on top of C++. If you are willing to use .NET there are things like Win2D which enables programming in the .NET framework (and hence accessing all the user interface frameworks seamlessly) and at the same time benefit from Direct2D hardware acceleration. However, this is not a widely used solution so it implies that you are not using a mainstream technology/library, which in my opinion is always a risky proposition in terms of support, documentation and future maintenance.
As mentioned, C++ user interfaces development is not as straightforward as it would be using the .NET framework. You can mix C++ and the .NET framework by using C++/CLI to gain access to WinForms (which is not a mainstream solution) or you could rely on the legacy ATL/MFC classes, which are still properly maintained despite they are considered legacy technology. MFC also forces you to deal with a lot of boilerplate, including a lot of Microsoft specific verbose classes that are now superseded by the C++ standard template libraries. You end up writing code which is so tightly coupled to Microsoft than it can no longer be considered portable C++.
On top of that, there is the possibility of using other cross-platform widget frameworks (such as wxWidgets or Qt), I personally dislike that approach as I think that if you need cross-platform Java is the simplest path, and if you want to focus on Windows using Microsoft mainstream tools is the safest way.
In any case, all those solutions involve a certain degree of boilerplate (sometimes they are actually quite complex to implement). Windows APIs never shined in terms of usability and simplicity. While .NET changed this paradigm and it is now pretty easy to build applications in C# or VB, it is still more focused on productivity applications where performance is not an issue.
In my opinion, if you need hardware acceleration, a medium complex user interface and a cross-platform solution there is nothing easier than Java with Swing and OpenGL. Java Swing is considered obsolete by many people, but the fact is that it still brings reasonably modern interfaces for desktop applications, it is quite lightweight and it powerful enough to provide a good user interface experience. Unless you are looking for a cutting-edge UI design, Java Swing will do the job. This is especially true for niche applications where usability and functionality are more relevant than interface look and feel.
I have reviewed all Java alternatives for OpenGL, and I finally chose JOGL. The reason is that JOGL is a general-purpose OpenGL implementation, not only focused on games as other libraries (such as LWJGL). It can be integrated with both AWT and Swing so you can have both of both worlds: custom hardware-accelerated graphics and easy to develop and implement user interfaces through Swing.
Using this combination of Java/Swing/OpenGL has the side benefit that your software shall run without any change in both Linux/UNIX and Windows systems. As a trade-off, if your software requires low-level data processing, Java fits sometimes a bit worse than C++ and it might be slightly slower too.
There are also alternative frameworks built on top of OpenGL. Those do not require anything else to have your user interfaces running, but according to my research, none of those interfaces is mature enough to be considered a safe alternative. They tend also to work only for specific OpenGL implementations and they also rely on other libraries so at the end there are still some external dependencies. The bindings for these frameworks are usually provided only for C++.
Without further delay, let’s code our first OpenGL program. I will be using Netbeans 8.2 and Oracle JDK 8.
JOGL can be downloaded from its main website https://jogamp.org and you will need to incorporate gluegen and jogl libraries to your IDE. Specific instructions on how to do this can be easily found so I will not repeat them here, especially because they will depend on the specific version you are installing and on the IDE you are using. Just suffice to say that you can download the Jars from the main JOGL website under Build/Downloads — Zip. Download “all platforms” .ZIP and the JavaDoc and install them in your project. You will end up with something like this:
First we will implement a class which extends JFrame so it will basically define a Window where we can draw using OpenGL.
This class:
Extends JFrame, because we want a stand-alone window where we can draw. This is not OpenGL related but just a way to have a Window in Java Swing.Implements GLEventListener interface, which is a requirement of JOGL to implement OpenGL. This interface requires implementing four methods: display, reshape, init and dispose.In this example, only display method is actually implemented. Here we will perform the actual drawing (more on this later).We need a GLProfile and a GLCapabilities objects which define which specific OpenGL version and which capabilities are being implemented.We need a GLCanvas object, which is basically a Canvas object that implements OpenGL. This will be added to a class which implements the GLEventListener interface, which in our case is our JFrame class. This is probably better understood by simply checking the three lines which create the GLCanvas object in the source code.
Extends JFrame, because we want a stand-alone window where we can draw. This is not OpenGL related but just a way to have a Window in Java Swing.
Implements GLEventListener interface, which is a requirement of JOGL to implement OpenGL. This interface requires implementing four methods: display, reshape, init and dispose.
In this example, only display method is actually implemented. Here we will perform the actual drawing (more on this later).
We need a GLProfile and a GLCapabilities objects which define which specific OpenGL version and which capabilities are being implemented.
We need a GLCanvas object, which is basically a Canvas object that implements OpenGL. This will be added to a class which implements the GLEventListener interface, which in our case is our JFrame class. This is probably better understood by simply checking the three lines which create the GLCanvas object in the source code.
To recap, we are basically creating a JFrame class which implements GLEventListener interface and incorporates a GLCanvas object. This allows us to use OpenGL over the GLCanvas object.
With all these steps we are ready to draw, which is achieved by implementing the display method.
The display method, in this case, is pretty simple. It receives a GLDrawable object as a parameter. From this object, we can get a GL2 object which is the one we use to draw.
To draw a line we just do:
gl.glBegin(GL2.GL_LINES);gl.glVertex3f(-0.50f, -0.50f, 0);gl.glVertex3f(0.50f, -0.50f, 0);gl.glEnd();
We enclose our drawing between glBegin and glEnd definitions. As glBegin parameter we provide GL2.GL_LINES which basically tells OpenGL that we are drawing a line. Because we are drawing a line we know that we need to provide two points (and OpenGL expects so). Therefore, we define two vertexes (points) which in this case are defined in 3D. These two points are the ones defining the line.
The basic idea is that we begin a drawing and provide the necessary information (in this case, two points to draw a line) that the GPU requires to draw it onto the Canvas object. Other drawing primitives would require other information between glBegin and glEnd.
To complete our triangle we just draw three segments.
Finally and in order to display the window, the JFrame needs to be invoked as any JFrame would be in Java Swing. Our main class would be as simple as this one:
This completes our first OpenGL program in Java. Comparatively, it is much easier to get a working example in Java than in C++, as Java Swing is easier to code than Microsoft existing APIs. In the next series we will continue programming OpenGL.