Bug 664

Summary: GLCanvas + GLJPanel Ignore Visibility (was OS X 10.8 + Java 7 + GLCanvas => Poor animation, lock ups, memory leak)
Product: [JogAmp] Jogl Reporter: Gene <gene.ressler>
Component: macosxAssignee: Sven Gothel <sgothel>
Status: RESOLVED FIXED    
Severity: critical    
Priority: ---    
Version: 2   
Hardware: pc_x86_64   
OS: macosx   
Type: --- SCM Refs:
1cee0f1ac437de952c5cc15d5a23c8c5ddfdda8a
Workaround: ---

Description Gene 2013-01-07 04:38:24 CET
Have a JOGL app that runs great with the last Apple JRE 6 and breaks with newest Oracle JRE 7.  Tried both JOGL rc11 and rc10.   

Symptom is that a GLCanvas animation in the Swing app refreshes only about 4 frames per second even though the app says it's rendering at 38.  Then when the animation is turned off with the GUI, which causes GLCanvas.setVisible(false) internally, GUI hangs, though no exception is raised.  Additionally, the VM image of the app starts growing rapidly as soon as the GLCanvas is shown and keeps growing quickly.

MacBook Pro, 15-inch, Late 2008 
2.8 GHz Intel Core 2 Duo 
OS X 10.8.2 (12C60) 
NVIDIA GeForce 9400M 256 MB 

JRE that works:
Java for Mac OS X (1.6.0_37-b06-434)

JRE that fails:
Java SE 1.7.0_10

I am guessing maybe Oracle had to use the OpenGL rendering pipeline for Java 2D because Apple didn't give them the Quartz pipeline, and this is conflicting with JOGL?  I can find zero info on the web about how Oracle Java 7 is rendering Java 2D on Mac OS X. I thought maybe fiddling with the rendering pipeline as you can with Windows and Linux might make a difference.

Below is the smallest useful example I can come up with.  Pressing the button should switch back and forth between a normal Swing JPanel with some text and the GLCanvas with a white square rotating smoothly on a black background.

With Java 6 it's fine.  With Java 7, the animation is not smooth (though it's much better than my full app with >30,000 triangles).  And when you press the button the second time, the text card does not reappear (GLCanvas setVisibility(false) is failing), and the VM image starts growing very quickly thereafter.

All help greatly appreciated.

import com.jogamp.opengl.util.FPSAnimator; 
import java.awt.*;
import java.awt.event.*;
import javax.media.opengl.*;
import javax.media.opengl.awt.GLCanvas;
import javax.swing.*; 

public class JOGLTest extends JFrame implements GLEventListener { 
   private CardLayout cards; 
   private static final String LABEL = "label"; 
   private static final String CANVAS = "canvas"; 
   private String selected = LABEL; 

   public JOGLTest() { 
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      GLProfile glp = GLProfile.get(GLProfile.GL2); 
      GLCapabilities caps = new GLCapabilities(glp); 
      GLCanvas canvas = new GLCanvas(caps); 
      canvas.setPreferredSize(new Dimension(640, 480)); 
      canvas.addGLEventListener(this); 
      final FPSAnimator animator = new FPSAnimator(canvas, 60); 
      JButton button = new JButton("Switch Cards"); 
      add(button, BorderLayout.NORTH); 
      final JPanel cardHolder = new JPanel(); 
      cards = new CardLayout(); 
      cardHolder.setLayout(cards); 
      cardHolder.add(new JLabel("A label to cover the canvas"), LABEL); 
      cardHolder.add(canvas, CANVAS); 
      add(cardHolder, BorderLayout.CENTER); 
      button.addActionListener(new ActionListener() { 
            public void actionPerformed(ActionEvent e) { 
                if (selected.equals(LABEL)) { 
                    animator.start(); 
                    cards.show(cardHolder, CANVAS); 
                    selected = CANVAS; 
                } 
                else { 
                    animator.stop(); 
                    cards.show(cardHolder, LABEL); 
                    selected = LABEL; 
                } 
            } 
        }); 
      pack(); 
      setTitle("OpenGL 2 Test"); 
   } 

   public static void main(String[] args) { 
       SwingUtilities.invokeLater(new Runnable() {
           public void run() {
                new JOGLTest().setVisible(true); 
           }
       });
   } 

   public void init(GLAutoDrawable drawable) { 
     GL2 gl = drawable.getGL().getGL2(); 
     gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 
   } 

   float spin = 0; 

   public void display(GLAutoDrawable drawable) { 
     GL2 gl = drawable.getGL().getGL2(); 
     gl.glClear(GL2.GL_COLOR_BUFFER_BIT); 
     gl.glPushMatrix(); 
     gl.glRotatef(spin, 0.0f, 0.0f, 1.0f); 
     gl.glColor3f(1.0f, 1.0f, 1.0f); 
     gl.glRectf(-25.0f, -25.0f, 25.0f, 25.0f); 
     gl.glPopMatrix(); 
     gl.glFlush(); 
     spin += 1; 
     while (spin > 360) spin -= 360; 
   } 

   public void reshape(GLAutoDrawable drawable, int x, int y, int w, int h) { 
     GL2 gl = drawable.getGL().getGL2(); 
     gl.glViewport(0, 0, w, h); 
     gl.glMatrixMode(GL2.GL_PROJECTION); 
     gl.glLoadIdentity(); 
     gl.glOrtho(-50.0, 50.0, 
         -50.0 * (float) h / (float) w, 
         50.0 * (float) h / (float) w, 
         -1.0, 1.0); 
     gl.glMatrixMode(GL2.GL_MODELVIEW); 
     gl.glLoadIdentity(); 
   } 

   public void dispose(GLAutoDrawable drawable) { } 
}
Comment 1 Gene 2013-01-07 05:30:42 CET
More investigation:

The animation uses a big FBO for a shadow map.  Turning off shadows causes most of the jerkiness to stop.  Frame rate goes back to normal.

I cannot reproduce the memory leak. Cross that off the list. 

The failure of the setVisible() call remains the same.  

Note that I had an earlier problem with setVisible() on a GLCanvas with the early version of JOGL 2, but it was on Intel HD Graphics displays only.  See bug 532:  https://jogamp.org/bugzilla/show_bug.cgi?id=532
Comment 2 Gene 2013-01-09 05:58:19 CET
Here is a smaller example that just shows how setVisible() fails.  There is no animation. If I do a remove(canvas) rather than serVisible(false), then the canvas disappears and can be restored with add(canvas, side).  However I can't use this as a workaround, since it causes other problems in some Windows machines, and this is a cross-platform app.

Thanks for any help you can provide.

import java.awt.*;
import java.awt.event.*;
import javax.media.opengl.*;
import javax.media.opengl.awt.GLCanvas;
import javax.swing.*; 

public class JOGLTest extends JFrame implements GLEventListener { 

   boolean there = true;
   
   public JOGLTest() {
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      GLProfile glp = GLProfile.get(GLProfile.GL2); 
      GLCapabilities caps = new GLCapabilities(glp); 
      final GLCanvas canvas = new GLCanvas(caps); 
      canvas.setPreferredSize(new Dimension(640, 480));
      canvas.addGLEventListener(this); 
      JButton button = new JButton("Make Invisible!"); 
      add(button, BorderLayout.NORTH); 
      add(canvas, BorderLayout.CENTER); 
      button.addActionListener(new ActionListener() { 
            public void actionPerformed(ActionEvent e) { 
                System.out.println("Make canvas invisible!");
                canvas.setVisible(false);
            } 
        }); 
      pack();
      setTitle("setVisible(false) fails!");       
   }
   
   public static void main(String[] args) {
       SwingUtilities.invokeLater(new Runnable() {
           public void run() {
                new JOGLTest().setVisible(true); 
           }
       });
   } 

   public void init(GLAutoDrawable drawable) { 
     GL2 gl = drawable.getGL().getGL2(); 
     gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 
   } 

   public void display(GLAutoDrawable drawable) { 
     GL2 gl = drawable.getGL().getGL2(); 
     gl.glClear(GL2.GL_COLOR_BUFFER_BIT); 
     gl.glPushMatrix(); 
     gl.glColor3f(1.0f, 1.0f, 1.0f); 
     gl.glRectf(-25.0f, -25.0f, 25.0f, 25.0f); 
     gl.glPopMatrix(); 
   } 

   public void reshape(GLAutoDrawable drawable, int x, int y, int w, int h) { 
     GL2 gl = drawable.getGL().getGL2(); 
     gl.glViewport(0, 0, w, h); 
     gl.glMatrixMode(GL2.GL_PROJECTION); 
     gl.glLoadIdentity(); 
     gl.glOrtho(-50.0, 50.0, 
         -50.0 * (float) h / (float) w, 
         50.0 * (float) h / (float) w, 
         -1.0, 1.0); 
     gl.glMatrixMode(GL2.GL_MODELVIEW); 
     gl.glLoadIdentity(); 
   } 

   public void dispose(GLAutoDrawable drawable) { } 
}
Comment 3 Sven Gothel 2013-02-16 20:16:26 CET
http://jogamp.org/git/?p=jogl.git;a=commit;h=1cee0f1ac437de952c5cc15d5a23c8c5ddfdda8a

The visibility bug of GLCanvas and GLJPanel is fixed in general
and I have tested on OSX Java6 and Java7 (latest) manually - all fine.

Memory leak in general has been addressed in Bug 691
and should be fixed - since it had been closed today as well.