LWJGL 3.0.0 : Swing performance for homemade Engine/Editor(Swing)

Started by jwar0, May 12, 2016, 15:20:31

Previous topic - Next topic

jwar0

Hi everyone,

Sorry, I didn't find a more explicit title, I will try to explain myself. Since my english is not really good I apologize for the next part ;)

For the last 6 months I tried to create my own 3D engine using Lwjgl.
My goals are :
   Understand Opengl programing from scratch(I've done the same with javascript, but it's really painfull)
   Create a small game with that knowledge
   and have fun :D

Until now I use the native window provided by GLFW, displaying 3D scene only and it works fine, but now that I have a small GUI editor(Swing) I try to optimize display of the Opengl Context inside Swing componant and have a question.
I use glReadPixels to copy data from Opengl context to a canvas but it take some CPU time, compare to the native windows is really slow:



Native3/4 %
Canvas + glReadPixels8/10 %

It's not much but I don't use full screen at the moment.
I guess native window is faster because data transfert is done within the Graphics card.

Some of the code
GL11.glReadPixels(0, 0, getWidth(), getHeight(), GL12.GL_BGR, GL11.GL_UNSIGNED_BYTE, pixelsBuffer);
				pixelsBuffer.get(((DataBufferByte)imageOff.getRaster().getDataBuffer()).getData());
				pixelsBuffer.flip();

				paintOGL();


I change the glReadPixels using native format GL_UNSIGNED_INT_8_8_8_8_REV and the copy is faster but I have to create a custom BufferedImage to manage this format that involve alpha which is slower than the previous version :(
GL11.glReadPixels(0, 0, getWidth(), getHeight(), GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, pixelsBuffer);
...
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
int[] nBits = {8, 8, 8, 8};
int[] bOffs = {2, 1, 0, 3};
ComponentColorModel colorModel = new ComponentColorModel(cs, nBits, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
WritableRaster raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
            										getWidth(), getHeight(),
            										getWidth()*4, 4,
            										bOffs, null);
imageOff = new BufferedImage(colorModel, raster, false, null);



Is there a way to speed up with swing or is it a dead end ?


Regards.

Kai

Please have a look at: https://github.com/httpdigest/lwjgl3-awt

It provides a (currently Windows-only) solution to render directly onto an AWT Canvas. Works also in Swing.

jwar0

Thanks for the link Kai.

I'm not into using someone else works, it's what I do at works with frameworks and proprietary solution with no choice :-\
It's not the best way to understand what I'm doing, and most of the time a lot of code for a specific need.

It' more like a abstract solution that I would implement, maybe I'm asking too much :D

jwar0

I change some part of the code and the performance improved.
The updated code if someone is interested.

The BufferedImage creation is made once at initialization

ColorModel colorModel = new DirectColorModel(32, 0x000000ff, 0x0000ff00, 0x00ff0000);
WritableRaster raster = colorModel.createCompatibleWritableRaster(getWidth(), getHeight());
imageOffInt = new BufferedImage(colorModel, raster, false, null);
...

GL11.glReadPixels(0, 0, getWidth(), getHeight(), GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, pixelsBufferInt);
pixelsBufferInt.get(((DataBufferInt)imageOffInt.getRaster().getDataBuffer()).getData());
pixelsBufferInt.flip();

paintOGL();


The paintOGL() method is an explicit call to prevent component.paint(Graphics g), which is empty, to do anything outside the main thread.

Kai

If you are really interested in the most efficient (fastest) solution possible, you might want to use the platform-specific APIs to connect a component surface (i.e. AWT Canvas) with OpenGL so that OpenGL directly renders onto that surface without the client needing a pipeline stall and readback via glReadPixels().
On Windows you would need WGL and on Linux (i.e. X11-based systems) you need GLX.

But what you would be doing then is to reimplement the lwjgl3-awt project. You could do that, if you prefer to learn how it's done and getting your hands really dirty :).
In that case there are plenty C examples on the internet when you google for "WGL OpenGL example" or "GLX OpenGL example" or the like.
And LWJGL3 exports Java-bindings to those platform-specific functions in the packages org.lwjgl.system.[linux/windows/macosx] as well as org.lwjgl.system.jawt (for interfacing with AWT's native library).

Also, the AWTGLCanvas of the LWJGL2 project (including all associated classes and native code) has some real interesting knowledge on how to go about all that with AWT.

Note that this solution is essentially exactly what the native GLFW window does. It sets an appropriate PixelFormat (or "Visual" in X11-speak) for the window/component surface and then creates an OpenGL context which is made current on that window.
This is also being done by the lwjgl3-awt project for AWT Canvas surfaces.

jwar0

I make up my mind, it' too much work for this project, and I haven't the skill to do that in a short time
I will stay on native GLFW solution if a finish a small game, and keep the code above to include OpenGL render in a Canvas.

Thanks ;)

asyx

glReadPixels is unbelievably slow. Like, you won't even believe how damn slow it is. You literally wait for the GPU for every single pixel. That's not what GPUs do. GPUs do as they please and you just throw data at them. Once you have to wait for them to finish, it's over.