Rendering slow after add/remove canvas cycles

Hi there,

when adding and removing a GLCanvas to/from a JFrame for several times, rendering dramatcially slows down after a few cycles. I tried this on two different computers, both equipped with an ATI card and running Win XP (didn’t happen under Linux, though). Does anyone know if this is another ATI driver problem or an OpenGL/JOGL issue?

Any comments wellcome!

Have a nice weekend –

Torsten

--------8<--------

Here’s the code I was running:


package test.jogl;

import java.awt.*;

import javax.swing.*;

import net.java.games.jogl.*;

public class GLCanvasTest extends JFrame 
implements GLEventListener {

  // How to rotate squares to form a cube's faces
  public static final float[][] FACE_ROTATION = {
      {90, 0, 1, 0},
      {0, 0, 1, 0, 0},
      {-90, 0, 1, 0},
      {-180, 0, 1, 0},
      {-90, 1, 0, 0},
      {90, 1, 0, 0}
  };

  // The cube's faces' colors
  public static final float[][] FACE_COLORS = {
      {1, 0, 0},
      {0, 1, 0},
      {0, 0, 1},
      {1, 1, 0},
      {1, 0, 1},
      {0, 1, 1}
  };
  
  // Rotation angles, to be modified when running
  private static double theta, phi;
  
  public static void main(String[] argv) {
    // Create and initialise a new instance
    GLCanvasTest t = new GLCanvasTest();
    t.setLocation(100, 100);
    t.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    t.getContentPane().setLayout(new BorderLayout());
    
    // Create and insert a JOGL canvas
    GLCapabilities caps = new GLCapabilities();
    GLCanvas canvas = GLDrawableFactory.getFactory().createGLCanvas(caps);
    canvas.setSize(600, 400);
    canvas.addGLEventListener(t);
    
    t.setVisible(true);
    // The loop provoking the weird behaviour 
    for (int i = 0; true; i++) {
      long t0 = System.currentTimeMillis();
      // Add the JOGL canvas
      t.getContentPane().add(canvas, BorderLayout.CENTER);
      t.pack();
      long t1 = System.currentTimeMillis();
      // Rotate the cube
      for (theta = 0; theta < 360; theta = theta + 10) {
        canvas.display();
        try {
          Thread.sleep(25);
        }
        catch (InterruptedException e) {}
        phi += 3;
      }
      long t2 = System.currentTimeMillis();
      // Remove the canvas
      t.getContentPane().remove(canvas);
      long t3 = System.currentTimeMillis();
      // Display required time 
      System.out.println("Round " + i + ": Adding took " + (t1 - t0) +  " ms, rendering took " + (t2 - t1) +  " ms, removing took " + (t3 - t2) + " ms.");
    }

  }

//--- Implementation of GLEventListener
  
  public void init(GLDrawable d) {
    GL gl = d.getGL();
    GLU glu = d.getGLU();

    gl.glEnable(GL.GL_DEPTH_TEST);
    gl.glMatrixMode(GL.GL_PROJECTION);
    gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

    glu.gluPerspective(60, d.getSize().getWidth()/d.getSize().getHeight(), 0.1, 10);
    glu.gluLookAt(0, 2, -5, 0, 0, 1, 0, 1, 0);
  }
  
  public void display(GLDrawable d) {
    GL gl = d.getGL();
    GLU glu = d.getGLU();
    
    gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

    gl.glMatrixMode(GL.GL_MODELVIEW);
    gl.glLoadIdentity();
    gl.glRotated(phi, 1, 0, 0);
    gl.glRotated(theta, 0, 1, 0);
    
    // Insert six squares and position them
    // to form a cube
    for (int i = 0; i < 6; i++) {
      gl.glPushMatrix();
      gl.glRotatef(
          FACE_ROTATION[i][0],
          FACE_ROTATION[i][1],
          FACE_ROTATION[i][2],
          FACE_ROTATION[i][3]
      );
      gl.glColor3fv(FACE_COLORS[i]);
      gl.glBegin(GL.GL_QUADS);
        gl.glVertex3d(-1, -1, 1);
        gl.glVertex3d(1, -1, 1);
        gl.glVertex3d(1, 1, 1);
        gl.glVertex3d(-1, 1, 1);
      gl.glEnd();
      gl.glPopMatrix();
    }
    
  }

  public void reshape(GLDrawable d, int x, int y, int width, int height) {
  }
  
  public void displayChanged(GLDrawable d, boolean modeChanged, boolean deviceChanged) {
  }

}

--------8<--------

And here’s a typical output:


Round 0: Adding took 234 ms, rendering took 985 ms, removing took 109 ms.
Round 1: Adding took 78 ms, rendering took 969 ms, removing took 109 ms.
Round 2: Adding took 63 ms, rendering took 1187 ms, removing took 125 ms.
Round 3: Adding took 47 ms, rendering took 1203 ms, removing took 110 ms.
Round 4: Adding took 46 ms, rendering took 1219 ms, removing took 110 ms.
Round 5: Adding took 46 ms, rendering took 1172 ms, removing took 125 ms.
Round 6: Adding took 47 ms, rendering took 1188 ms, removing took 109 ms.
Round 7: Adding took 47 ms, rendering took 1203 ms, removing took 125 ms.
Round 8: Adding took 47 ms, rendering took 1203 ms, removing took 109 ms.
Round 9: Adding took 157 ms, rendering took 3093 ms, removing took 125 ms.
Round 10: Adding took 141 ms, rendering took 3922 ms, removing took 109 ms.
Round 11: Adding took 0 ms, rendering took 4063 ms, removing took 109 ms.
Round 12: Adding took 125 ms, rendering took 4000 ms, removing took 110 ms.
Round 13: Adding took 140 ms, rendering took 3953 ms, removing took 110 ms.
Round 14: Adding took 140 ms, rendering took 4000 ms, removing took 110 ms.

It works fine for me on my NVidia-based notebook. It sounds similar to problems seen on ATI cards with OpenGL context destruction and re-creation. Are you running the latest JOGL build? What gets printed if you run with -Djogl.verbose?

One obvious issue is that you are removing the Canvas from the component hierarchy from the main thread, which strictly speaking is not allowed; you’re supposed to make widget hierarchy modifications from the AWT event queue thread. Does this version work better?


import java.awt.*;
 
import javax.swing.*;
 
import net.java.games.jogl.*;
 
public class GLCanvasTest2 extends JFrame  
  implements GLEventListener {
 
  // How to rotate squares to form a cube's faces
  public static final float[][] FACE_ROTATION = {
    {90, 0, 1, 0},
    {0, 0, 1, 0, 0},
    {-90, 0, 1, 0},
    {-180, 0, 1, 0},
    {-90, 1, 0, 0},
    {90, 1, 0, 0}
  };
 
  // The cube's faces' colors
  public static final float[][] FACE_COLORS = {
    {1, 0, 0},
    {0, 1, 0},
    {0, 0, 1},
    {1, 1, 0},
    {1, 0, 1},
    {0, 1, 1}
  };
   
  // Rotation angles, to be modified when running
  private static double theta, phi;
   
  public static void main(String[] argv) {
    // Create and initialise a new instance
    final GLCanvasTest2 t = new GLCanvasTest2();
    t.setLocation(100, 100);
    t.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    t.getContentPane().setLayout(new BorderLayout());
     
    // Create and insert a JOGL canvas
    GLCapabilities caps = new GLCapabilities();
    final GLCanvas canvas = GLDrawableFactory.getFactory().createGLCanvas(caps);
    canvas.setSize(600, 400);
    canvas.addGLEventListener(t);
     
    t.setVisible(true);
    // The loop provoking the weird behaviour  
    for (int i = 0; true; i++) {
      long t0 = System.currentTimeMillis();
      // Add the JOGL canvas
      try {
        EventQueue.invokeAndWait(new Runnable () {
            public void run() {
              t.getContentPane().add(canvas, BorderLayout.CENTER);
              t.pack();
            }
          });
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
      long t1 = System.currentTimeMillis();
      // Rotate the cube
      for (theta = 0; theta < 360; theta = theta + 10) {
        canvas.display();
        try {
          Thread.sleep(25);
        }
        catch (InterruptedException e) {}
        phi += 3;
      }
      long t2 = System.currentTimeMillis();
      // Remove the canvas
      try {
        EventQueue.invokeAndWait(new Runnable () {
            public void run() {
              t.getContentPane().remove(canvas);
            }
          });
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
      long t3 = System.currentTimeMillis();
      // Display required time  
      System.out.println("Round " + i + ": Adding took " + (t1 - t0) +  " ms, rendering took " + (t2 - t1) +  " ms, removing took " + (t3 - t2) + " ms.");
    }
 
  }
 
  //--- Implementation of GLEventListener
   
  public void init(GLDrawable d) {
    GL gl = d.getGL();
    GLU glu = d.getGLU();
 
    gl.glEnable(GL.GL_DEPTH_TEST);
    gl.glMatrixMode(GL.GL_PROJECTION);
    gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
 
    glu.gluPerspective(60, d.getSize().getWidth()/d.getSize().getHeight(), 0.1, 10);
    glu.gluLookAt(0, 2, -5, 0, 0, 1, 0, 1, 0);
  }
   
  public void display(GLDrawable d) {
    GL gl = d.getGL();
    GLU glu = d.getGLU();
     
    gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
 
    gl.glMatrixMode(GL.GL_MODELVIEW);
    gl.glLoadIdentity();
    gl.glRotated(phi, 1, 0, 0);
    gl.glRotated(theta, 0, 1, 0);
     
    // Insert six squares and position them
    // to form a cube
    for (int i = 0; i < 6; i++) {
      gl.glPushMatrix();
      gl.glRotatef(
                   FACE_ROTATION[i][0],
                   FACE_ROTATION[i][1],
                   FACE_ROTATION[i][2],
                   FACE_ROTATION[i][3]
                   );
      gl.glColor3fv(FACE_COLORS[i]);
      gl.glBegin(GL.GL_QUADS);
      gl.glVertex3d(-1, -1, 1);
      gl.glVertex3d(1, -1, 1);
      gl.glVertex3d(1, 1, 1);
      gl.glVertex3d(-1, 1, 1);
      gl.glEnd();
      gl.glPopMatrix();
    }
     
  }
 
  public void reshape(GLDrawable d, int x, int y, int width, int height) {
  }
   
  public void displayChanged(GLDrawable d, boolean modeChanged, boolean deviceChanged) {
  }
} 

Hi Ken!

Thanks for trying out the code on your NVidia notebook!

It sounds similar to problems seen on ATI cards with
OpenGL context destruction and re-creation. Are you
running the latest JOGL build? What gets printed if you
run with -Djogl.verbose?

The debug option yields the following output:


JOGL version 1.1.0-b11
Using single-threaded workaround of dispatching display() on event thread
Using single-threaded workaround of dispatching display() on event thread
Using single-threaded workaround of dispatching display() on event thread
Round 0: Adding took 282 ms, rendering took 1187 ms, removing took 110 ms.
Using single-threaded workaround of dispatching display() on event thread
Round 1: Adding took 62 ms, rendering took 1203 ms, removing took 110 ms.
Using single-threaded workaround of dispatching display() on event thread
Round 2: Adding took 46 ms, rendering took 1219 ms, removing took 110 ms.
Using single-threaded workaround of dispatching display() on event thread
Round 3: Adding took 46 ms, rendering took 1188 ms, removing took 109 ms.
Using single-threaded workaround of dispatching display() on event thread
Round 4: Adding took 63 ms, rendering took 1203 ms, removing took 109 ms.
Using single-threaded workaround of dispatching display() on event thread
Round 5: Adding took 47 ms, rendering took 969 ms, removing took 109 ms.
Using single-threaded workaround of dispatching display() on event thread
Round 6: Adding took 63 ms, rendering took 1187 ms, removing took 110 ms.
Using single-threaded workaround of dispatching display() on event thread
Round 7: Adding took 47 ms, rendering took 1203 ms, removing took 109 ms.
Using single-threaded workaround of dispatching display() on event thread
Round 8: Adding took 47 ms, rendering took 1187 ms, removing took 110 ms.
Using single-threaded workaround of dispatching display() on event thread
Round 9: Adding took 94 ms, rendering took 3156 ms, removing took 172 ms.
Using single-threaded workaround of dispatching display() on event thread

Looks pretty normal, doesn’t it?

One obvious issue is that you are removing the Canvas
from the component hierarchy from the main thread,
which strictly speaking is not allowed; you’re supposed
to make widget hierarchy modifications from the AWT
event queue thread. Does this version work better?

Ooops… you’re right, of course! However, performing canvas adding/removing in the event-dispatch thread doesn’t change a thing, so this apparently isn’t the problem. It really looks like this is an ATI Windows driver issue… One can’t do much about this, can one?

Thanx again –

Torsten

Hi,

just saw the announcement of 1.1b12 being released… The problem remains – as expected, if this is an ATI Win issue.

Bye –

Torsten

Hello,
I also have this problem : when I add and remove glcanvas several times, my rendering speed decrease. I have the version of Jogl 1.1b12.
But the problem isn’t present on all computers.

With an ATI Radeon 9200 series, the problem comes the third time I add the glcanvas to a panel
With an nvidia geforce4mx 420, the problem comes the second time I add the glcanvas
And with a NVIDIA Quadro 2 Pro, there isn’t any problems…

All computers are under Windows XP.

This should be fixed in the sources in the CVS repository. There is a more recent build than 1.1 b12 for Windows under the Temporary Files folder in “jogl-win32.zip”. Does this fix the issue for you?

Thank you very much! With this new version, I don’t have problems any more. :slight_smile: