java "invalid drawable"

Hello, joglers!
I’m having a problem with Jogl on Mac OS X, Panther.
I’m workin on an app that can programatically create new GLCanavses, and display them in tabbed panes. It works fine on Win XP, Mac Tiger, Linux, but it fails miserably on a standard Panther system. I’m getting the following error:

2005-08-30 05:08:01.628 java[3897] invalid drawable

From what I’ve read on other posts and google searches, this error happens because OpenGL has its own rendering thread, separate from the Swing display thread, and from the AWT threads. It then tries to draw on a view that hasn’t appeared yet.
What I don’t know is how to programatically test whether the view has been create, so I can draw on it.

Does anyone understand this issue better than this? I’d love some light-shedding comments.

Thanks,
Andrei

Hello

If I ve understood your problem, I think you should try to launch your application with the option: -Dsun.java2d.noddraw=true

for exemple: java -Dsun.java2d.noddraw=true MyApp

Hope that could help you

I’m afraid trying to disable direct draw usage on OSX won’t do very much at all :wink:

Yes, -Dsun.java2d.noddraw=true only has an effect on Windows.

Are you running the latest JOGL version (1.1.1)? Some issues with GLCanvas initialization on Mac OS X were fixed toward the end of the 1.1 release train which specifically made adding GLCanvases to a JTabbedPane work. Do you have a test case of the program which isn’t working?

Hi. Thanks for the responses.

“-Dsun.java2d.noddraw=true” doesn’t work, just like you said.

I have upgraded to jogl 1.1.1, and the same problem happens.
I have a reproducible bug for this, but I can’t post the code. I’ll start sandboxing, and post a simple example.

Thank you,
Andrei

Hi, everyone !

Here’s a simple sandbox example of this bug. Clicking on the glcanvas should create a new tab with the same thing drawn. Instead, on Mac OS X Panther, I’m getting an “invalid drawable” error.

Here’s the code:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;

import net.java.games.jogl.GL;
import net.java.games.jogl.GLCanvas;
import net.java.games.jogl.GLCapabilities;
import net.java.games.jogl.GLDrawable;
import net.java.games.jogl.GLDrawableFactory;
import net.java.games.jogl.GLEventListener;
import net.java.games.jogl.GLU;

public class InvalidDrawableSandbox extends JFrame implements GLEventListener, MouseListener
{
public static final Dimension PREFERRED_FRAME_SIZE = new Dimension (450,375);
JTabbedPane jtp;
int tabIndex = 0;

public InvalidDrawableSandbox() 
{
    super ("Invalid Drawable Sandbox");   
    // put the panel inside a JTabbedPane
    jtp = new JTabbedPane (); 
    jtp.add(makeNewPanel (), "Canvas #" + tabIndex);
    
	this.getContentPane().add(jtp, BorderLayout.CENTER);
}

JPanel makeNewPanel ()
{
    // put the new canvas inside a JPanel
    JPanel jp = new JPanel ();
    GLCapabilities capabilities = new GLCapabilities();
    GLCanvas canvas = GLDrawableFactory.getFactory().createGLCanvas(capabilities);
    canvas.addGLEventListener(this);
    canvas.addMouseListener(this);
    jp.setLayout(new BorderLayout());
    jp.add (canvas, BorderLayout.CENTER);
    
		tabIndex++;
		return jp;
}

public Dimension getPreferredSize () 
{
    return PREFERRED_FRAME_SIZE;
}

public static void main (String[] args) 
{
		InvalidDrawableSandbox f = new InvalidDrawableSandbox();
    f.pack();
    f.show();
}

public void displayChanged (GLDrawable drawable, boolean b1, boolean b2) {}

public void display (GLDrawable drawable) 
{
	GL gl = drawable.getGL();
	GLU glu = drawable.getGLU();
	gl.glClear (GL.GL_COLOR_BUFFER_BIT);
	drawFilledRect (gl, Color.cyan.darker());
}

// Called after OpenGL is init'ed
public void init (GLDrawable drawable) 
{
    GL gl = drawable.getGL();
    gl.glClearColor( 0.80f, 1.0f, 0.8f, 1.0f ); //white 
    gl.glColor3f( 0.0f, 0.2f, 0.0f ); 
    //gl.glPointSize(4.0f);
}

public void reshape (GLDrawable drawable, int x, int y, int width, int height) 
{
    GL gl = drawable.getGL(); 
    GLU glu = drawable.getGLU(); 
    gl.glViewport( 0, 0, width, height ); 
    gl.glMatrixMode( GL.GL_PROJECTION );  
    gl.glLoadIdentity(); 
    glu.gluOrtho2D( 0.0, 450.0, 0.0, 375.0); 
}

 protected void drawFilledRect (GL gl, Color c) 
 {
    float redF = c.getRed() / 255f;
    float greenF = c.getGreen() / 255f;
    float blueF = c.getBlue() / 255f;
    gl.glColor3f (redF, greenF, blueF);
    gl.glRecti (200, 50, 250, 150);
    // now reset the color to black
    gl.glColor3f (0.0f, 0.0f, 0.0f);
}  

 // mouse listener implementation
public void mousePressed(MouseEvent e) 
{
	jtp.add(makeNewPanel (), "Canvas #" + tabIndex);
}

public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}

}

I would appreciate any suggestions.

Thanks,
Andrei

Your test program works fine for me on a G5 with an NVidia GeForce FX 5200 running 10.3.9 and JOGL 1.1.1. Have you verified you are using the latest build of JOGL (both the jar file and native library)? What specific hardware, including graphics card, are you running on?

Hi, Ken ! Thanks for your interes in my problem.

I’m running on Mac OS X 10.3.9, 933 Mhz G4, 448 MB RAM, GeForce 4 MX with 64 MB RAM.
I’m using jogl 1.1.1: the jogl.jar in the Eclipse project, and the 2 natives in ~naron/Java/Extensions/.

Do you have any idea what actually causes the error? It seems that it’s not a java exception, but rather something from underneath ?

Thank you,
Andrei

I have a similar machine at home (GeForce 4 MX) and will try your test case there. Is there any change in behavior if you launch your test case from the command line, outside the Eclipse IDE?

You’re right, the error is not a Java-level exception but something coming from far down in Apple’s graphics stack. Double-check to make sure you are using the correct version of libjogl.jnilib and jogl.jar.

Yup, I’ve just double-checked: I downloaded the jogl.jar and mac os x natives to a folder, and typed:

java -classpath jogl.jar:. InvalidDrawableSandbox -Djava.library.path libjogl.jnilib:libjogl_cg.jnilib

The same problem persisted.

Andrei

Ok, so I’ve narrowed it down even more, it’s NOT the display function that messes up. The same problem happens even when display() is never called, and there are no GLEventListeners at all:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;

import net.java.games.jogl.GLCanvas;
import net.java.games.jogl.GLCapabilities;
import net.java.games.jogl.GLDrawableFactory;

public class InvalidDrawableSandbox extends JFrame implements MouseListener // , GLEventListener
{
public static final Dimension PREFERRED_FRAME_SIZE = new Dimension (450,375);
JTabbedPane jtp;
GLCanvas canvas;
int tabIndex = 0;

public InvalidDrawableSandbox() 
{
    super ("Invalid Drawable Sandbox");   
    // put the panel inside a JTabbedPane
    jtp = new JTabbedPane (); 
    jtp.add(makeNewPanel (), "Canvas #" + tabIndex);
    
	this.getContentPane().add(jtp, BorderLayout.CENTER);
}

JPanel makeNewPanel ()
{
    // put the new canvas inside a JPanel
    JPanel jp = new JPanel ();
    GLCapabilities capabilities = new GLCapabilities();
    GLCanvas canvas = GLDrawableFactory.getFactory().createGLCanvas(capabilities);
    
    canvas.addMouseListener(this);
    jp.setLayout(new BorderLayout());
    jp.add (canvas, BorderLayout.CENTER);
    
    this.canvas = canvas;
		tabIndex++;
		return jp;
}

public Dimension getPreferredSize () 
{
    return PREFERRED_FRAME_SIZE;
}

public static void main (String[] args) 
{
		InvalidDrawableSandbox f = new InvalidDrawableSandbox();
    f.pack();
    f.show();
}

 // mouse listener implementation
public void mousePressed(MouseEvent e) 
{
	JPanel p = makeNewPanel();
	jtp.add(p, "Canvas #" + tabIndex);
	jtp.setSelectedIndex(this.tabIndex-1);
	//canvas.addGLEventListener(this);
}

public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}

}

The GLCanvas is not calling display, but it ‘is’ displaying a black background over the initial panel. However, on the first click, when a new tab should appear, it just fails with 'invalid drawable".

Thanks,
Andrei

Your test case works fine on my machine. I’m not sure whether there are any typos in your command line above but it isn’t doing what you expect. I still think you probably have an old libjogl.jnilib sitting around somewhere. The command

java InvalidDrawableSandbox

should fail, and

java -classpath jogl_dir/jogl.jar -Djava.library.path=jogl_dir InvalidDrawableSandbox

should succeed. That’s the only way to make sure you’re using exactly the jar file and native library file you expect.

Ken, I’ve just tried what you recommended:

// ===============================================
myMachine:~/Desktop/test naron$ java InvalidDrawableSandbox
Exception in thread “main” java.lang.NoClassDefFoundError: net/java/games/jogl/GLEventListener
at java.lang.ClassLoader.defineClass0(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:539)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:123)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:251)
at java.net.URLClassLoader.access$100(URLClassLoader.java:55)
at java.net.URLClassLoader$1.run(URLClassLoader.java:194)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:187)
at java.lang.ClassLoader.loadClass(ClassLoader.java:289)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:274)
at java.lang.ClassLoader.loadClass(ClassLoader.java:235)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:302)
// ===============================================

and

// ================================================
myMachine:~/Desktop/test naron$ java -classpath ./jogl.jar:. -Djava.library.path=. InvalidDrawableSandbox
2005-08-31 23:20:52.216 java[7075] invalid drawable
// ================================================

where ~/Desktop/test has the following contents:
// ================================================
myMachine:~/Desktop/test naron$ ls
InvalidDrawableSandbox.class libjogl.jnilib
InvalidDrawableSandbox.java libjogl_cg.jnilib
jogl.jar
// ================================================

I’ve also tried this on one other Panther computer (a one year old iBook) and got the same problem.

Thanks,
Andrei

My next step is to go into the jogl sources. Do you have any idea where would be a good starting point for the origins of the “invalid drawable” message?

Thank you,
Andrei

One more check. Could you please cd into a different directory than all of those files and

jogl -classpath jogl_dir:jogl_dir/jogl.jar InvalidDrawableSandbox

This time it should fail with an UnsatisfiedLinkError. If not, you have a libjogl.jnilib sitting around somewhere.

A simpler check might be to

cd /
sudo find . -name libjogl.jnilib

Yup, that’s what I get:

myMachine:~/Desktop naron$ java -classpath test/jogl.jar:. InvalidDrawableSandboxException in thread “main” java.lang.UnsatisfiedLinkError: no jogl in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1517)
at java.lang.Runtime.loadLibrary0(Runtime.java:788)
at java.lang.System.loadLibrary(System.java:834)
at net.java.games.jogl.impl.NativeLibLoader$1.run(NativeLibLoader.java:72)
at java.security.AccessController.doPrivileged(Native Method)
at net.java.games.jogl.impl.NativeLibLoader.load(NativeLibLoader.java:58)
at net.java.games.jogl.impl.GLContext.(GLContext.java:52)
at net.java.games.jogl.impl.macosx.MacOSXGLContextFactory.createGLContext(MacOSXGLContextFactory.java:60)
at net.java.games.jogl.GLCanvas.(GLCanvas.java:72)
at net.java.games.jogl.GLDrawableFactory.createGLCanvas(GLDrawableFactory.java:150)
at net.java.games.jogl.GLDrawableFactory.createGLCanvas(GLDrawableFactory.java:118)
at net.java.games.jogl.GLDrawableFactory.createGLCanvas(GLDrawableFactory.java:85)
at InvalidDrawableSandbox.makeNewPanel(InvalidDrawableSandbox.java:42)
at InvalidDrawableSandbox.(InvalidDrawableSandbox.java:32)
at InvalidDrawableSandbox.main(InvalidDrawableSandbox.java:60)

Andrei

OK, thanks for confirming that.

As I don’t see this problem on the machines I have available, if you want to narrow down where the error is coming from, I would recommend starting from the JSR-231 sources:

cvs co -r JSR-231 -P jogl jogl-demos

(The -P gets rid of old empty directories.)

The build instructions are the same although the APIs are slightly different. The implementation is very different and hopefully should be more correct than the current JOGL sources. The Mac OS X native code for interacting with the window system is in jogl/src/native/jogl/MacOSXWindowSystemInterface.m and you should be able to add printfs around the operations which operate on the NSView. It’s probably one of those operations which is failing.

Further investigation:

adding a “canvas.setSize(new Dimension(220, 220));” line right after the GLCanvas creation will fix the bug in the sandbox example, even though it won’t actually set the size to 220, 220.

Ken, could this have something to do with a fix to this documented issue ?

// ============
There are two possible solutions, both application-specific. The best and most portable appears to be to put the GLCanvas into a JPanel and set the JPanel’s preferred size to (0, 0). The JPanel will cause this constraint to be enforced on its contained GLCanvas. The other workaround is to call setPreferredSize(new Dimension(0, 0)) on a newly-created GLCanvas; this method is new in 1.5.
// ============

Thanks,
Andrei

Hmmm. It isn’t directly related to that but a change was fairly recently made in the Mac OS X context handling to remove a seemingly-bogus test against a window size of (0, 0) to determine whether the view was ready to draw or not. I wonder whether that test is necessary on some configurations, although in theory it should not be. Could you contact me via email (my address is kbr on java.net) and try a test build or two to try to address this?