Search |
||||||||||||||||||||||||||||||||||||||
Integrating GLPbuffer and Java Graphics2D
Thu, 2008-10-30
|
||||||||||||||||||||||||||||||||||||||
| Class | Estimated Time of Full Animation | Estimated Time Per Frame |
| GLCanvas | 39285ms=39.285sec | 78.57ms=.07857sec |
| GLJPanel | 42325ms=42.325sec | 84.65ms=.08465sec |
| GLPbuffer | 50945ms=50.945sec | 101.89ms=.10189sec |
| Class | Estimated Time of Full Animation | Estimated Time Per Frame |
| GLCanvas | 48795ms=48.795sec | 97.59ms=.09759sec |
| GLJPanel | 53161ms=53.161sec | 106.322ms=.106322sec |
| GLPbuffer | 82221ms=82.221sec | 164.442ms=.164442sec |
| Class | Estimated Time of Full Animation | Estimated Time Per Frame |
| GLCanvas | 50813ms=50.813sec | 101.626ms=.101626sec |
| GLJPanel | 67704ms=67.704sec | 135.408ms=.135.408sec |
| GLPbuffer | 70703ms = 70.703sec | 141.406ms=.141406sec |
These tables show that the time penalty for using
GLPbuffer is not huge, although it is more noticeable
on Mac than on other platforms. So we decided to attempt
integration using pbuffers and take a chance on its experimental
nature. Code for the programs used to produce these tables can be
found in the sample code download. Also
included is an interactive program that allows you to compare the
two techniques interactively.
Since we can get a BufferedImage from a pbuffer,
then integration with the already existing math visualization framework was relatively simple. All we had to do was set up an OpenGL
renderer to draw to the pbuffer, get the buffered image from the
buffer, and then copy the BufferedImage to the screen.
Here we explain how to do this.
To create the pbuffer, we used the following code, which our OpenGL renderer calls before drawing the picture (with the width and height being that of the window).
if (buf == null || bufw != width || bufh != height){
if (buf != null) { // clean the old buffer
context.destroy(); //context is type GLContext
buf.destroy(); // buf is type GLPbuffer
}
GLDawableFactory fac = GLDrawableFactory.getFactory();
GLCapabilities glCap = new GLCapabilities();
// Without line below, there is an error on Windows.
glCap.setDoubleBuffered(false);
//makes a new buffer
buf = fac.createGLPbuffer(glCap, null, width, height, null);
//save size for later use in getting image
bufw = width;
bufh = height;
//required for drawing to the buffer
context = buf.createContext(null);
}
A context is an abstraction that is necessary for rendering to any OpenGL drawable. The JOGL event-driven drawing framework handles contexts automatically, but they are required for drawing outside that framework, which is the case with our pbuffer. Manually controlling a context involves getting a context from the drawable object and making it current when rendering to it.
We also discovered a buffer clean-up check was needed, as follows:
if (buf != null) {
context.destroy();
buf.destroy();
}
We found that this is necessary to prevent slowdowns and
crashes from the creation of too many pbuffers. The
destroy() method of each frees up resources being held
by the context and the pbuffer. This is apparently not done
automatically in JOGL.
Another check that should also be made at the beginning of the
program is to make sure that a GLPbuffer can be
created:
if(!GLDrawableFactory.getFactory().canCreateGLPbuffer())//Disable the using of OpenGL or at least by way of aPbuffer
This check is necessary because a pbuffer isn't necessarily
supported on all computers, since it uses the hardware to accelerate
rendering. The check looks to see if the graphics card on the
computer supports the pbuffer but from our testing on our
different lab machines, we have found that trying to create a
GLPbuffer sometimes prduces an error even though this method
returns true. Knowing this, it might be better to just try to
create the pbuffer and catch any exception that occurs.
Now, to render to the pbuffer, we take the context created for the buffer and make it current. This assures that the OpenGL commands that follow will draw to the pbuffer and not elsewhere. In our OpenGL renderer, we call this at the start of the drawing method of the renderer:
context.makeCurrent();
The next task is to is to get a GL object to draw
with. This is done directly using the pbuffer and the rest follows
the normal JOGL syntax.
GL gl = buf.getGL();
Getting an image from the pbuffer is done by use of the JOGL
class Screenshot, which is used for taking screen shots
of OpenGL applications. In spite of the name, it only returns an
image of the current OpenGL drawable, not of the entire screen. By
calling Screenshot.readToBufferedImage(bufw,bufh), we
take a screen shot of the current GL Drawable (our pbuffer) as a
buffered image. Also note that the context for our pbuffer must be
current. Now that we have the buffered image, we then can use Java
Graphics2Dto draw the image to the screen.
context.makeCurrent();
BufferedImage img = Screenshot.readToBufferedImage(bufw,bufh);
context.release();
g.drawImage(img,0,0,null);
Since the design of 3D-XplorMath-J was meant for use with Java
Graphics2D, this is the most beneficial part of using
a pbuffer in the project. Since we're able to get a buffered image
from the pbuffer, no code changes were needed outside of the
renderer except for creating an OpenGL renderer to do the 3D
drawing instead of a Graphics2D renderer.
Since mathematical objects can be complicated, rendering these objects can become quite slow, especially when rotating a surface and having it render in real time. Since being able to rotate and look at mathematical objects in as many ways as possible is largely a goal of the 3D-XplorMath project, we needed a way to increase the efficiency of real-time rendering of the math objects. Part of the rendering inefficiency could be attributed to the use of a pbuffer, but the drawing was still too slow given the capabilities of OpenGL. To start improving render time, we began looking at OpenGL display lists and how they are used and what kind of speed up we might achieve with them.
Display lists allow the storage of OpenGL commands in an optimized form for later execution, which is largely useful when used to store geometry or state changes that will be used multiple times, such as in the rotation of a mathematical object. This is useful in this case, since the same display list can be called each time the rotating object is drawn. While this is all well and good for code organization, does it actually give us the speed boost we want?
A display list is identified by a unique integer, which is
created by using the gl.glGenList() command where gl
is an object of type GL. Next we create a new list
using the list identifier and the desired mode.
displayListID = gl.glGenLists(1); // type int
gl.glNewList(displayListID, GL.GL_COMPILE);
The OpenGL commands that follow glNewList are added
to the list, and when the commands to be stored in the list are
complete, we simply end the list using
gl.glEndList();
Finally, when we want to execute the cached code in the display list we just call the list using its identifier.
gl.glCallList(displayListID);
After learning how to use a display list, we decided that we should write a test to see if it actually increases efficiency before we worked out how to add it to the 3D OpenGL renderer. Taking our test prgram that draws 500 spheres in 500 frames from before, we implemented the same test with the addition of a display list to store the commands to draw a sphere, and we called the list to draw each sphere to the screen. What we found was a surprisingly huge speed-up.
The simple sphere drawing program using just a pbuffer and no display list for drawing 500 different frames of 500 spheres ran in about 50 seconds in a Linux environment. The same program implemented using a display list ran in an impressive 12 seconds, making the decision to incorporate this into the project an easy one.
Adding display lists to 3D-XplorMath-J was a relatively simple venture. The idea is that code for rendering the object is put into a display list and when the renderer needs to draw to the screen, it does so by calling the display list. This means that when the object just needs to be transformed, such as in rotating the object, we can just apply the transformations to the display list rather than re-rendering the object with its new orientation.
Use of a display list gave us a huge speed increase for rotation
of surfaces, but the original production of the display list was
still rather slow. We discovered that this was because we were
drawing individual polygons using glBegin(GL_POLYGON).
We got a speed up by using glBegin(GL_QUAD_STRIP) to
draw our surfaces. A GL_QUAD_STRIP is a geometric
primitive that is a linked strip of quadrilaterals defined by two
vertexes at a time. This slight change turned out to be a
significant speed improvement in comparison to drawing the
individual polygons.
When we look at the speedup achieved by implementing these two techniques to the project, the penalty for using a pbuffer becomes small in comparison. We now have a renderer that performs real-time rendering well for even fairly complicated geometric objects.
In Java SE 6, JOGL and Java 2D can be used together directly.
Java 6 allows Swing objects to now overlay OpenGL rendering. Now
rendering of 3D OpenGL graphics can be done directly onto Java 2D
graphics, and similarly, Java 2D graphics can be drawn directly
onto 3D OpenGL graphics. For now, the 3D-XplorMath-J project will
continue to use Java 5, but the future switch to Java 6 gives many
design options for integration of OpenGL rendering that are more
reliable and more efficient. Java 6 makes using either a
GLJPanel or a GLCanvas fairly painless, which implies a small speedup from no longer having to get buffered images from a pbuffer.
Eliminating the use of the pbuffer would avoid the complications
with not being able to create pbuffers on some computers, making
the OpenGL rendering version available to more people. Java 6 is
definitely a great advancement for graphics and the Java
programming language and should be looked into for the use of
OpenGL with Java. But for programs that still need Java 5, a pbuffer
is one option for using OpenGL and Java 2D until the switch is
made.
|
|