Extreme slowdown

I have a Swing application with a GLCanvas. In this canvas I render some simple geometry using a raycasting shader.
This shader is slow so the output is limited to around 2 fps, with no CPU usage. The output speed is no problem,
but the slowdown of the Swing part of my application is not acceptable.

I believe this is because OpenGL is blocking in the EvenDispatch. Are there any workarounds or solutions?
I’ve tried all the command line options available in the Threading class, but none of them seems to work.

What platform and graphics card are you using?

In general JOGL performs all of its work on the event dispatch thread due to multithreading-related stability issues on all platforms. If you are on the Windows platform you should be able to get some more parallelism out of your app by specifying -Dopengl.1thread=false on the command line. This won’t help on X11 platforms by default because due to how and when GLX tokens are sent with various vendors’ OpenGL drivers there is some locking which needs to occur which will prevent the event dispatch thread from processing events when your Animator calls GLCanvas.display(). You can try disabling this behavior with -Djogl.GLContext.optimize on the command line but you’re on your own at that point. There are known issues with using this flag and ATI’s proprietary Linux drivers.

If you know that your GUI isn’t changing (components being added/removed at random times), you can also control the threading behavior of your application manually using the GLContext and GLDrawable APIs explicitly. The caveat about the X11 locking behavior still applies in this case, but you may be able to make the OpenGL context once on one thread and leave it current for the lifetime of the application, which may simplify things for you.

Currently I’m using Windows with NVidia and ATI. And I expect to use Linux soon as well,
but if I can get it to work on Windows I’m happy. I tried setting the flags you mentioned and they
didn’t seem to have any effect. I don’t add or remove GUI components at random,
but I do some Graphics painting in another component.

I don’t think I understood the part about “making the context in one thread and leave it current…”.
It sounds like what I want: start a rendering thread that just works independently of Swing.
But I’m not sure of how to do that, or what the limitations of that is.

Since the general speed of my application is so low anyway I thought I maybe use a PBuffer instead, so I did this:

GLPbuffer glPbuffer = GLDrawableFactory.getFactory().createGLPbuffer(glCapabilities, null, width, height, null);
glPbuffer.addGLEventListener(this);

and created a custom Animator that just did glPbuffer.display() but the speed increase was minimal.
Shouldn’t this way avoid the AWT pipeline?

Part of the problem is that it is not just the GUI of my application that is slow, the whole OS is slow.

I’ve created an example to illustrate the performance hit on the OS even though the application isn’t using any CPU resources.

Please try it yourself and tell me if you get the same results or if I’m doing something wrong.


import com.sun.opengl.util.GLUT;

import javax.media.opengl.*;
import static javax.media.opengl.GL.*;
import javax.media.opengl.glu.GLU;
import javax.swing.*;
import java.awt.*;

/**
 * Example to illustrate the choppyness of the OS while the application is not using CPU resources.
 */
public class SlowDownTest implements GLEventListener {
    private String[] vertexShader = new String[]{
            "varying vec3 normal;\n" +
                    "void main() {\n" +
                    "   normal = normalize(gl_Normal);\n" +
                    "   gl_Position = ftransform();\n" +
                    "}"};
    private String[] fragmentShader = new String[]{
            "varying vec3 normal;\n" +
                    "void main (void) {\n" +
                    "   vec3 N = normalize(normal);\n" +
                    "   vec4 final_color = vec4(0, 0, 0, 0);\n" +
                    "   float f = 0.0;\n" +
                    "   for(int i = 0; i < 220; i++) {\n" +
                    "       for(int j = 0; j < 150; j++) {\n" +
                    "          f = f + float(i) / float(j + 1);\n" +                   
                    "       }\n" +
                    "   }\n" +
                    "   for(int i = 0; i < 220; i++) {\n" +
                    "       for(int j = 0; j < 150; j++) {\n" +
                    "          final_color.rgb = N.xyz * 0.5 + 0.5;\n" +
                    "          final_color.a = 1.0 * f;\n" +
                    "       }\n" +
                    "   }\n" +
                    "   gl_FragColor = final_color;\n" +
                    "}"};
    private int width = 500;
    private int height = 400;
    private GLCanvas glCanvas;

    private float stepSize = (float) (Math.PI / 16);
    private float rot = 0;
    private int program;

    public SlowDownTest() {
        GLCapabilities glCapabilities = new GLCapabilities();
        glCanvas = new GLCanvas(glCapabilities);

        Dimension size = new Dimension(width, height);
        glCanvas.setSize(size);
        glCanvas.setPreferredSize(size);
        glCanvas.setMinimumSize(size);
        glCanvas.addGLEventListener(this);

        JFrame frame = new JFrame("Slowdown Shader Test");
        frame.getContentPane().add(glCanvas);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);

        CustomAnimator animator = new CustomAnimator();
        animator.start();
    }

    public void init(GLAutoDrawable glAutoDrawable) {
        GL gl = glAutoDrawable.getGL();

        gl.glClearColor(0, 0, 0, 1);
        gl.glEnable(GL_DEPTH_TEST);
        gl.glShadeModel(GL_SMOOTH);
        gl.glEnable(GL_NORMALIZE);
        gl.glFrontFace(GL_CCW);

        gl.glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
        gl.glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
        gl.glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

        createShaders(gl);
    }

    private void createShaders(GL gl) {
        System.out.println("Compiling Shaders.");
        int vertexShaderObject = gl.glCreateShader(GL_VERTEX_SHADER);
        int fragmentShaderObject = gl.glCreateShader(GL_FRAGMENT_SHADER);

        program = gl.glCreateProgram();

        gl.glShaderSource(vertexShaderObject, vertexShader.length, vertexShader, null);
        gl.glShaderSource(fragmentShaderObject, fragmentShader.length, fragmentShader, null);
        gl.glAttachShader(program, vertexShaderObject);
        gl.glAttachShader(program, fragmentShaderObject);

        gl.glCompileShader(vertexShaderObject);
        gl.glCompileShader(fragmentShaderObject);

        gl.glLinkProgram(program);
        System.out.println("Compiled!");
    }

    public void display(GLAutoDrawable glAutoDrawable) {
        GL gl = glAutoDrawable.getGL();
        gl.glClearColor(0, 0, 0, 1);

        gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        gl.glPushMatrix();

        gl.glTranslatef(0, 0, -3);
        gl.glRotatef(rot, 0, 1, 0);

        gl.glUseProgram(program);

        GLUT glut = new GLUT();
        glut.glutSolidTeapot(1);

        gl.glUseProgram(0);
        gl.glPopMatrix();

        rot += stepSize;
    }

    public void reshape(GLAutoDrawable glAutoDrawable, int x, int y, int width, int height) {
        GL gl = glAutoDrawable.getGL();
        gl.glViewport(x, y, width, height);

        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();

        GLU glu = new GLU();
        glu.gluPerspective(60, (float) width / height, 0.1f, 1000f);
        gl.glMatrixMode(GL.GL_MODELVIEW);
    }

    public void displayChanged(GLAutoDrawable glAutoDrawable, boolean b, boolean b1) {
    }

    public static void main(String[] args) {
        SlowDownTest test = new SlowDownTest();
    }

    public class CustomAnimator implements Runnable {
        private boolean running = true;
        private Thread thread = null;

        public CustomAnimator() {
            thread = new Thread(this);
        }

        public synchronized void start() {
            thread.start();
        }

        public synchronized void stop() {
            running = false;
            while (thread != null) {
                //Make sure run completes
                try {
                    wait();
                } catch (InterruptedException ie) {
                    //
                }
            }
        }

        public void run() {
            while (running) {
                if (glCanvas != null) {
                    glCanvas.display();
                } else {
                    running = false;
                }
                try {
                    Thread.sleep(15);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            synchronized (CustomAnimator.this) {
                thread = null;
                CustomAnimator.this.notify();
            }
        }
    }
}

PS: Increase or decrease the number of iterations in the fragment shader to get the right effect. ;D

I’ve checked if the same problem occurs with a C++ program and to my surprise it did.
So it is not a JOGL problem but a driver/general OpenGL problem.

This is a threading problem, is that true?

So your slow operation is done on the EDT, which gums up the UI.

Why can’t your slow OGL operation be rendered off the EDT in its own low-priority thread onto a separate buffer which is then copied to the screen by the EDT?

Keith
PS: I know nothing about J/OGL but a little about the EDT

Actually my conclusion is that it won’t matter if I do that since the whole computer is slowed down.
If you can try the example you will see that it is more than the UI that is slow.

Are you sure the Shader is not executed by the CPU due to feature-emulation? This would explain the slowdonw of the whole system.

The GLSL on my ATI card says that both the Vertex and Fragment shader will run in hardware.
In addition there is almost no CPU usage during rendering.
The same type of test was done using C++ with the same results.

(I have an ATI x1600 and Nvidia Geforce 7900 GT so there shouldn’t be any features missing. :slight_smile: )