GLJPanel performance impact, and sun.java2d.opengl

Hello all,

I’m keen on using Swing components, including GLJPanels. I’m seeing a bigger performance hit than I expected though on my simple demo (a texture mapped Earth):

GLCanvas: 90fps
GLJPanel: 13fps

Profiling the GLJPanel confirms that large chunks of CPU time are being eaten by:

sun.java2d.loops.Blit.Blit: (33.38%)
net.java.games.jogl.impl.x11.X11GLImpl.glReadPixels: 16.06%

(The percentages are not accurate; they should be multiplied by about 1.5 since my retarded profiler counts cycles in waitForEvents.)

I have two questions:

  • It’s surprising to me that I’m losing so much performance. Is there anything I should check for?

  • Sun is doing a lot of work to accelerate Swing components using OpenGL (enabled with -Dsun.java2d.opengl=True). I believe they are using a pbuffer to back Swing components and letting the videocard blit them. It seems like there could be a wonderful intersection between GLJPanel and Sun’s OpenGL implementation: if only they could play together nicely, perhaps the extra copies would become unnecessary…

At the moment, JOGL apps and -Dsun.java2d.opengl=True do not seem to like each other at all; I almost immediately get “Xlib: unexpected async reply (sequence 0x304)” after starting the app, and no graphical output.

On a related subject: I’ve noticed that the backing pbuffers of an GLJPanel never seem to shrink after having been enlarged. This is somewhat unfortunate for performance if the user temporarily enlarges a window.

Another unexpected anomaly with GLJPanel and GLCanvas:

I have a JOGL app which has a CPU-intensive thread, with thread priority set to minimum. It is otherwise busy 100% of the time. The model I’m displaying is only being redrawn when the user rotates the display manually.

With GLCanvas as my display component, everything works as expected. It’s fast, the GUI thread (which has mouse controls to spin the objects in the GUI) is very responsive, and my CPU-bound thread makes relatively steady progress… a bit slower when I’m extensively manipulating the GUI, of course.

With GLJPanel, not only is the GUI thread very unresponsive, but the CPU-intensive thread seems to hiccup-- completely stop making any progress-- for periods up to maybe a second at a time. (Very commonly delays of ~100ms, with longer delays less likely.)

For example, I zoom the GUI, and it can take a second for the screen to update. The model here is 180 OpenGL points. Nothing else. So I figured the CPU-bound thread must be getting priority and it’s just a scheduling thing.

Not so. The CPU-bound thread has stopped running too for that second or two where the screen is stuck. During this interval, CPU usage is pegged at 100% as well.

It’s as though the GLJPanel’s readPixels operation is blocking all of Java, simultaneously consuming huge amounts of CPU, and taking a second or two. That’s speculation because I don’t really know what’s causing the problem.

A related observation is that it is very hard to get a GLJPanel to act with as much priority as a GLCanvas does. Even with a CPU-bound thread, the GLCanvas thread seems to have very high priority and even the awt events seem to come through faster. On the GLJPanel, even if I drop the CPU-bound thread priority to minimum, I can’t get it to give much CPU time to GUI as I’d like to.

The massive slowdown is very much an expected behavior. PBuffers are slow. glReadPixels is very slow. There just isn’t a whole lot we can do about it. It would be really nice to be able to point a BufferedImage to a location in memory(PBO) and totally avoid the copy from the videocard to java.

I agree that it would be wonderful if Sun’s Java2D OpenGL pipeline and JOGL could interoperate. I have been and am continuing to push the Java2D group to expose certain functionality in the JAWT which would allow JOGL and Java2D to share the same back buffer. Until that is the case, we’re basically stuck with pixel readbacks and blitting to the screen using BufferedImage.

Sorry about the issues with -Dsun.java2d.opengl=true; in the JSR-231 implementation we will try to make JOGL and the Java2D OpenGL pipeline at least coexist better if not interoperate better.

I’ll see whether I can accelerate the blitting of the BufferedImage to the GLJPanel in the next JOGL release.

I don’t have a lot of other advice for you at this point. Take a look at the GLJPanel’s source code. The code path you’re taking is implementable with the public JOGL API, so you could make your own pbuffer and read the pixels back yourself if you can find some more efficient or application-specific way of doing so. You might also want to look into EXT_framebuffer_object, which is becoming available in more recent drivers, for doing offscreen rendering.

I’m kinda surprised that BufferedImages are used for blitting.

Check the sourcecode for BufferedImage and see what a call like setRGB(…) actually does. It does per-pixel conversions, even per-component, falling through many methods, all dead slow.

I thought we are reading plain RGB data from the pbuffer, so we could just use MemorySourceImage with it’s newPixels() and setAnimated(true).

A while ago I took screenshots with java.awt.Robot in java-code and I noticed performance was crap. I narrowed it down to the BufferedImage and used the native Peer directly to get the pixel-data (Robot uses that method, then puts all data in BufferedImage). It was at least 20x faster.

I have probably missed something here, but I think it’s worth testing this a bit (MemorySourceImage) as it’s not much work, and might result in much better performance.

GLJPanel doesn’t use setRGB(), it fetches the Raster out of the BufferedImage, fetches the DataBuffer out of the raster, and the array of pixels out of the DataBuffer. As far as I know, MemoryImageSource and the ImageProducer/ImageConsumer paths are not as fast as putting the pixels directly into a BufferedImage.

To be honest, it’s not really a problem. More of a curiosity. I’d kind of hoped that GLJPanel would be faster if I managed to enable them, but I would have been surprised :slight_smile:

You could use a GLCanvas in Swing by putting it inside a JPanel and use Heavyweight popups and tooltips


JPopupMenu.setDefaultLightWeightPopupEnabled(false);
ToolTipManager.sharedInstance().setLightWeightPopupEnabled(true);

you might need to implement some window/component events handlers to force the GLCanvas to behave properly. Also I read someone had to override the getPreferredSize() method of the containing JPanel to return [0,0] for propper TabbedPane behaviour, but it should work in most situations.