Own Animator class

Hi,

I stumbled across a strange problem I have no explanation for. I used an Animator object to animate multiple display windows; works well. But for some reasons, I want my own Animator class. So I got the Animator.java from the src archive. I commented this nvidia fix out because this “glcanvas.willSetRenderingThread” method wasn’t available (for reasons I don’t know) and I have an ATI video card. But my animator class only works with one window. Using more than 2 windows it crashes completely, giving me:

Using ATI workaround of dispatching display() on event thread
2004-09-03 19:00:46.297 java[15099] *** _NSAutoreleaseNoPool(): Object 0x3ca5f0 of class NSSurface autoreleased with no pool in place - just leaking
Using ATI workaround of dispatching display() on event thread
apple.awt.EventQueueExceptionHandler Caught Throwable : java.lang.IllegalMonitorStateException: current thread not owner
java.lang.IllegalMonitorStateException: current thread not owner
at net.java.games.jogl.impl.JAWT_DrawingSurface.Unlock0(Native Method)
at net.java.games.jogl.impl.JAWT_DrawingSurface.Unlock(JAWT_DrawingSurface.java:61)
at net.java.games.jogl.impl.macosx.MacOSXOnscreenGLContext.unlockSurface(MacOSXOnscreenGLContext.java:253)
at net.java.games.jogl.impl.macosx.MacOSXOnscreenGLContext.free(MacOSXOnscreenGLContext.java:161)
at net.java.games.jogl.impl.GLContext.invokeGL(GLContext.java:319)
at net.java.games.jogl.impl.macosx.MacOSXOnscreenGLContext.invokeGL(MacOSXOnscreenGLContext.java:81)
at net.java.games.jogl.GLCanvas.displayImpl(GLCanvas.java:208)
at net.java.games.jogl.GLCanvas.display(GLCanvas.java:75)
at net.java.games.jogl.GLCanvas.paint(GLCanvas.java:82)
at sun.awt.RepaintArea.paint(RepaintArea.java:194)
at apple.awt.ComponentModel.handleEvent(ComponentModel.java:281)
at java.awt.Component.dispatchEventImpl(Component.java:3744)
at java.awt.Component.dispatchEvent(Component.java:3543)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:456)
at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:234)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:178)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:170)
at java

I’m using Mac OS X 10.3, jdk 1.4.2, jogl b1.5.
Is there any reason why this happens? Is this nvidia fix somehow relevant for ati cards as well or does there happen something I’m not aware of?

Regards,
Ralf Ebert

I think this might be because you are calling display() from two different threads, ie each animator.

Try looking at NeHe tutorial 42 http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=42

HTH,
SNR

The strange thing is that it works with the “original” compiled Animator class but not with the one I copied from the original source. It isn’t the multiple thread issue as well, when I call both display methods from only one Animator thread (actually, that’s what I’m going to do for better view control) it doesn’t help…

Regards,
Ralf

This should work. If you can boil this down into a small and self-contained test case please file a bug with the JOGL Issue Tracker on the JOGL home page.

As I’m quite sure that this is a case of “stupid me” I’m posting the example code here. For some reasons, this does work even with the original Animator only sometimes (but it worked all the time in the original application). This seems to be some kind of timing issue, because sometimes it works, sometimes I get the second canvas running, sometimes I get the first frame of the second canvas. Thanks in advance for your help…

package com.dreivier.test;

import java.awt.EventQueue;
import java.awt.Frame;

import net.java.games.jogl.Animator;
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.GLException;
import net.java.games.jogl.GLU;
import net.java.games.jogl.impl.SingleThreadedWorkaround;

public class AnimatorTest {

public AnimatorTest() {

}

public static void main(String[] args) {
    
    GLDrawableFactory factory = GLDrawableFactory.getFactory();
    
    Frame frame1 = new Frame("screen 1");
    GLCanvas canvas1 = factory.createGLCanvas(new GLCapabilities());
    canvas1.addGLEventListener(new MyRenderer());
    frame1.add(canvas1);
    frame1.setSize(640, 480);
    frame1.show();

    
    Frame frame2 = new Frame("screen 2");
    GLCanvas canvas2 = factory.createGLCanvas(new GLCapabilities());
    canvas2.addGLEventListener(new MyRenderer());
    frame2.add(canvas2);
    frame2.setSize(640, 480);
    frame2.show();

    
    
    MyAnimator ani1 = new MyAnimator(canvas1);
    ani1.start();

    MyAnimator ani2 = new MyAnimator(canvas2);
    ani2.start();
}

}

class MyAnimator {
private GLDrawable drawable;

private Runnable runnable;

private Thread thread;

private boolean shouldStop;



/** Creates a new Animator for a particular drawable. */
public MyAnimator(GLDrawable drawable) {
    this.drawable = drawable;

    // Workaround for NVidia driver bug 80174
    if (drawable instanceof GLCanvas) {
        //((GLCanvas) drawable).willSetRenderingThread();
    }
}



/** Starts this animator. */
public synchronized void start() {
    if (thread != null) {
        throw new GLException("Already started");
    }
    if (runnable == null) {
        runnable = new Runnable() {
            public void run() {
                boolean noException = false;
                try {
                    // Try to get OpenGL context optimization since we know
                    // we
                    // will be rendering this one drawable continually from
                    // this thread; make the context current once instead of
                    // making it current and freeing it each frame.
                    drawable.setRenderingThread(Thread.currentThread());

                    // Since setRenderingThread is currently advisory
                    // (because
                    // of the poor JAWT implementation in the Motif AWT,
                    // which
                    // performs excessive locking) we also prevent
                    // repaint(),
                    // which is called from the AWT thread, from having an
                    // effect for better multithreading behavior. This call
                    // is
                    // not strictly necessary, but if end users write their
                    // own animation loops which update multiple drawables
                    // per
                    // tick then it may be necessary to enforce the order of
                    // updates.
                    drawable.setNoAutoRedrawMode(true);

                    while (!shouldStop) {
                        noException = false;
                        drawable.display();
                        noException = true;
                    }
                } finally {
                    shouldStop = false;
                    drawable.setNoAutoRedrawMode(false);
                    try {
                        // The surface is already unlocked and rendering
                        // thread is already null if an exception occurred
                        // during display(), so don't disable the rendering
                        // thread again.
                        if (noException) {
                            // Destruction of the underlying GLContext may
                            // have
                            // disabled the setRenderingThread optimization
                            // out
                            // from under us
                            if (drawable.getRenderingThread() != null) {
                                drawable.setRenderingThread(null);
                            }
                        }
                    } finally {
                        synchronized (MyAnimator.this) {
                            thread = null;
                            MyAnimator.this.notify();
                        }
                    }
                }
            }
        };
    }
    thread = new Thread(runnable);
    thread.start();
}



/**
 * Stops this animator, blocking until the animation thread has finished.
 */
public synchronized void stop() {
    shouldStop = true;
    // It's hard to tell whether the thread which calls stop() has
    // dependencies on the Animator's internal thread. Currently we
    // use a couple of heuristics to determine whether we should do
    // the blocking wait().
    if ((Thread.currentThread() == thread)
            || (SingleThreadedWorkaround.doWorkaround() && EventQueue.isDispatchThread())) {
        return;
    }
    while (shouldStop && thread != null) {
        try {
            wait();
        } catch (InterruptedException ie) {
        }
    }
}

}

class MyRenderer implements GLEventListener {

private int width;

private int height;

private boolean b;



public MyRenderer() {

}



public void init(GLDrawable drawable) {

}



public void reshape(GLDrawable drawable, int x, int y, int width, int height) {
    this.width = width;
    this.height = height;
}



public void display(GLDrawable drawable) {

    final GL gl = drawable.getGL();
    final GLU glu = drawable.getGLU();

    gl.glClear(GL.GL_COLOR_BUFFER_BIT);

    gl.glLoadIdentity();
    gl.glViewport(0, 0, width, height);
    gl.glMatrixMode(GL.GL_PROJECTION);
    gl.glLoadIdentity();

    glu.gluOrtho2D(0.0, width, 0.0, height);
    gl.glPushMatrix();

    if (b)
        gl.glColor4f(1.0f, 0.0f, 0.0f, 1f);
    else
        gl.glColor4f(0.0f, 1.0f, 0.0f, 1f);
    b = !b;

    gl.glBegin(GL.GL_QUADS);
    gl.glVertex2i(0, 0);
    gl.glVertex2i(0, 12);
    gl.glVertex2i(12, 12);
    gl.glVertex2i(12, 0);
    gl.glEnd();

    gl.glPopMatrix();
    
    gl.glFlush();
    gl.glFinish();
}




public void displayChanged(GLDrawable drawable, boolean modeChanged, boolean deviceChanged) {
}

}

I don’t have a Mac in front of me at the moment but your test program seems to work fine on Windows. Could you please go to the JOGL home page and file a bug with the Issue Tracker (attaching your test case above) so we can keep track of this issue? Thanks.

Already done, issue 109. Would be very great if there would be a solution for this anytime soon… Thanks in advance!

Sorry for asking again; did somebody tried this and got the same problem (especially under Mac OS X) or know what could be done about that? I have no deep understanding of the OpenGL rendering internals yet; is there some solution for this problem?

Thanks!

This will probably be of little help, but with the nightly build from 10/17, I get a window, then:

[quote]Using ATI workaround of dispatching display() on event thread
java.lang.IllegalMonitorStateException: current thread not owner
at net.java.games.jogl.impl.JAWT_DrawingSurface.Unlock0(Native Method)
at net.java.games.jogl.impl.JAWT_DrawingSurface.Unlock(JAWT_DrawingSurface.java:61)
at net.java.games.jogl.impl.macosx.MacOSXOnscreenGLContext.unlockSurface(MacOSXOnscreenGLContext.java:253)
at net.java.games.jogl.impl.macosx.MacOSXOnscreenGLContext.free(MacOSXOnscreenGLContext.java:161)
at net.java.games.jogl.impl.GLContext.invokeGL(GLContext.java:319)
at net.java.games.jogl.impl.macosx.MacOSXOnscreenGLContext.invokeGL(MacOSXOnscreenGLContext.java:84)
at net.java.games.jogl.GLCanvas.displayImpl(GLCanvas.java:208)
at net.java.games.jogl.GLCanvas.display(GLCanvas.java:75)
at MyAnimator$1.run(AnimatorTest.java:113)
at java.lang.Thread.run(Thread.java:552)
Bus error
[/quote]

my OS/X application requires 2 canvases to be rendered independently (with Animator-like updating) within the same Frame. i just got the FPSAnimator working for the application by using only 1 FPSAnimator for both of the canvases (was receiving the same “not owner” error when i had multiple animators in the app). ive only started testing it but it looks good so far and no strange threading errors.

There is a bug on the JOGL Issues page (which is down right now, so I can’t check the number) related to multiple threads and OS X. I am pretty sure there is a low-level bug in the JAWT on OS X that prevents proper multithreaded support of JOGL. If you use the JOGL code in the CVS repository and specify -DJOGL_SINGLE_THREADED_WORKAROUND=true then multiple Animators work fine on OS X. This flag will be automatically enabled for ATI cards in 1.1 b08 (as it is currently, but the enabling will be done very early in the initialization sequence on Windows in particular).