aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xmake/scripts/tests-x64.bat5
-rwxr-xr-xmake/scripts/tests.sh7
-rw-r--r--src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java73
-rw-r--r--src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java241
-rw-r--r--src/jogl/classes/javax/media/opengl/GLAutoDrawable.java183
-rw-r--r--src/jogl/classes/javax/media/opengl/awt/GLCanvas.java62
-rw-r--r--src/jogl/classes/javax/media/opengl/awt/GLJPanel.java63
-rw-r--r--src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java49
-rw-r--r--src/jogl/classes/jogamp/opengl/GLDrawableHelper.java306
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextDrawableSwitchNEWT.java64
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java11
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/RedSquareES2.java11
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/TextureSequenceCubeES2.java1
13 files changed, 876 insertions, 200 deletions
diff --git a/make/scripts/tests-x64.bat b/make/scripts/tests-x64.bat
index c8bc03017..586b65175 100755
--- a/make/scripts/tests-x64.bat
+++ b/make/scripts/tests-x64.bat
@@ -14,12 +14,13 @@ REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestSharedCon
REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestSharedContextListNEWT2 %*
REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestSharedContextVBOES2NEWT %*
REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.acore.TestGLProfile01NEWT %*
-REM scripts\java-win32-dbg.bat com.jogamp.opengl.test.junit.jogl.acore.TestGLProfile02NEWT %*
+REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.acore.TestGLProfile02NEWT %*
+scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.acore.TestGLContextDrawableSwitchNEWT %*
REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.demos.gl2.newt.TestGearsNewtAWTWrapper %*
REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.demos.gl2.newt.TestGearsNEWT -time 30000
REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.demos.es1.newt.TestGearsES1NEWT %*
-scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT %*
+REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT %*
REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT -vsync -time 4000 -x 10 -y 10 -width 100 -height 100 -screen 0
REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT -vsync -time 40000 -width 100 -height 100 -screen 0 %*
REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.awt.TestAWTCardLayoutAnimatorStartStopBug532 %*
diff --git a/make/scripts/tests.sh b/make/scripts/tests.sh
index ef64aa7dd..1fa687a21 100755
--- a/make/scripts/tests.sh
+++ b/make/scripts/tests.sh
@@ -146,6 +146,7 @@ function jrun() {
#D_ARGS="-Djogl.debug.Animator"
#D_ARGS="-Dnativewindow.debug=all"
#D_ARGS="-Djogl.debug.GLCanvas"
+ #D_ARGS="-Djogl.debug.GLCanvas -Djogl.debug.Animator"
#D_ARGS="-Djogl.debug.GLContext -Dnativewindow.debug.X11Util.XSync"
#D_ARGS="-Dnativewindow.debug.X11Util.XSync -Dnativewindow.debug.ToolkitLock.TraceLock"
#D_ARGS="-Dnativewindow.debug.NativeWindow"
@@ -246,7 +247,7 @@ function testawtswt() {
#testnoawt com.jogamp.newt.opengl.GLWindow $*
#testnoawt com.jogamp.opengl.test.junit.jogl.offscreen.TestOffscreen01GLPBufferNEWT $*
#testnoawt com.jogamp.opengl.test.junit.jogl.offscreen.TestOffscreen02BitmapNEWT $*
-testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT $*
+#testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT $*
#testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestElektronenMultipliziererNEWT $*
#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestFloatUtil01MatrixMatrixMultNOUI $*
#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestPMVMatrix01NEWT $*
@@ -266,7 +267,7 @@ testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT $*
#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestSharedContextVBOES1NEWT $*
#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestSharedContextVBOES2NEWT $*
#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLAutoDrawableDelegateOnOffscrnCapsNEWT $*
-#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLContextDrawableSwitchNEWT $*
+testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLContextDrawableSwitchNEWT $*
#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestPointsNEWT $*
#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLAutoDrawableGLWindowOnOffscrnCapsNEWT $*
@@ -349,6 +350,8 @@ testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT $*
#testawt com.jogamp.opengl.test.junit.jogl.awt.TestBug461PBufferSupersamplingSwingAWT
#testawt com.jogamp.opengl.test.junit.jogl.glu.TestBug463ScaleImageMemoryAWT $*
#testawt com.jogamp.opengl.test.junit.jogl.awt.TestAWTCardLayoutAnimatorStartStopBug532 $*
+#testawt com.jogamp.opengl.test.junit.jogl.awt.TestGLCanvasAWTActionDeadlock00AWT $*
+#testawt com.jogamp.opengl.test.junit.jogl.awt.TestGLCanvasAWTActionDeadlock01AWT $*
#
# swt (testswt)
diff --git a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java
index 33322628d..ea794cc78 100644
--- a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java
+++ b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java
@@ -216,6 +216,26 @@ public class GLCanvas extends Canvas implements GLAutoDrawable {
}
};
+ private class DisposeGLEventListenerAction implements Runnable {
+ private GLEventListener listener;
+ private boolean remove;
+ private DisposeGLEventListenerAction(GLEventListener listener, boolean remove) {
+ this.listener = listener;
+ this.remove = remove;
+ }
+
+ @Override
+ public void run() {
+ final RecursiveLock _lock = lock;
+ _lock.lock();
+ try {
+ listener = helper.disposeGLEventListener(GLCanvas.this, drawable, context, listener, remove);
+ } finally {
+ _lock.unlock();
+ }
+ }
+ };
+
/**
* Storage for the client area rectangle so that it may be accessed from outside of the SWT thread.
*/
@@ -302,7 +322,7 @@ public class GLCanvas extends Canvas implements GLAutoDrawable {
addPaintListener(new PaintListener() {
@Override
public void paintControl(final PaintEvent arg0) {
- if ( !helper.isExternalAnimatorAnimating() ) {
+ if ( !helper.isAnimatorAnimatingOnOtherThread() ) {
display(); // checks: null != drawable
}
}
@@ -439,13 +459,45 @@ public class GLCanvas extends Canvas implements GLAutoDrawable {
}
@Override
- public void addGLEventListener(final GLEventListener arg0) {
- helper.addGLEventListener(arg0);
+ public void addGLEventListener(final GLEventListener listener) {
+ helper.addGLEventListener(listener);
}
@Override
- public void addGLEventListener(final int arg0, final GLEventListener arg1) throws IndexOutOfBoundsException {
- helper.addGLEventListener(arg0, arg1);
+ public void addGLEventListener(final int idx, final GLEventListener listener) throws IndexOutOfBoundsException {
+ helper.addGLEventListener(idx, listener);
+ }
+
+ @Override
+ public int getGLEventListenerCount() {
+ return helper.getGLEventListenerCount();
+ }
+
+ @Override
+ public GLEventListener getGLEventListener(int index) throws IndexOutOfBoundsException {
+ return helper.getGLEventListener(index);
+ }
+
+ @Override
+ public boolean getGLEventListenerInitState(GLEventListener listener) {
+ return helper.getGLEventListenerInitState(listener);
+ }
+
+ @Override
+ public void setGLEventListenerInitState(GLEventListener listener, boolean initialized) {
+ helper.setGLEventListenerInitState(listener, initialized);
+ }
+
+ @Override
+ public GLEventListener disposeGLEventListener(GLEventListener listener, boolean remove) {
+ final DisposeGLEventListenerAction r = new DisposeGLEventListenerAction(listener, remove);
+ runInGLThread(r);
+ return r.listener;
+ }
+
+ @Override
+ public GLEventListener removeGLEventListener(final GLEventListener listener) {
+ return helper.removeGLEventListener(listener);
}
/**
@@ -496,18 +548,13 @@ public class GLCanvas extends Canvas implements GLAutoDrawable {
public boolean invoke(final boolean wait, final GLRunnable run) {
return helper.invoke(this, wait, run);
}
-
+
@Override
- public void removeGLEventListener(final GLEventListener arg0) {
- helper.removeGLEventListener(arg0);
+ public void enqueue(GLRunnable glRunnable) {
+ helper.enqueue(glRunnable);
}
@Override
- public GLEventListener removeGLEventListener(int index) throws IndexOutOfBoundsException {
- return helper.removeGLEventListener(index);
- }
-
- @Override
public void setAnimator(final GLAnimatorControl arg0) throws GLException {
helper.setAnimator(arg0);
}
diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java b/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java
new file mode 100644
index 000000000..8197be4f5
--- /dev/null
+++ b/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java
@@ -0,0 +1,241 @@
+/**
+ * Copyright 2012 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package com.jogamp.opengl.util;
+
+import javax.media.opengl.GLAnimatorControl;
+import javax.media.opengl.GLAutoDrawable;
+import javax.media.opengl.GLContext;
+import javax.media.opengl.GLDrawable;
+import javax.media.opengl.GLEventListener;
+import javax.media.opengl.GLRunnable;
+
+import jogamp.opengl.Debug;
+
+/**
+ * Providing utility functions dealing w/ {@link GLDrawable}s, {@link GLAutoDrawable} and their {@link GLEventListener}.
+ */
+public class GLDrawableUtil {
+ protected static final boolean DEBUG = Debug.debug("GLDrawable");
+
+ public static final boolean isAnimatorStartedOnOtherThread(GLAnimatorControl animatorCtrl) {
+ return ( null != animatorCtrl ) ? animatorCtrl.isStarted() && animatorCtrl.getThread() != Thread.currentThread() : false ;
+ }
+
+ public static final boolean isAnimatorStarted(GLAnimatorControl animatorCtrl) {
+ return ( null != animatorCtrl ) ? animatorCtrl.isStarted() : false ;
+ }
+
+ public static final boolean isAnimatorAnimatingOnOtherThread(GLAnimatorControl animatorCtrl) {
+ return ( null != animatorCtrl ) ? animatorCtrl.isAnimating() && animatorCtrl.getThread() != Thread.currentThread() : false ;
+ }
+
+ public static final boolean isAnimatorAnimating(GLAnimatorControl animatorCtrl) {
+ return ( null != animatorCtrl ) ? animatorCtrl.isAnimating() : false ;
+ }
+
+ /**
+ * Moves the designated {@link GLEventListener} from {@link GLAutoDrawable} <code>src</code> to <code>dest</code>.
+ * If <code>preserveInitState</code> is <code>true</code>, it's initialized state is preserved
+ * and {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)} issued w/ the next {@link GLAutoDrawable#display()} call.
+ * <p>
+ * Note that it is only legal to pass <code>preserveInitState := true</code>,
+ * if the {@link GLContext} of both <code>src</code> and <code>dest</code> are shared, or has itself moved from <code>src</code> to <code>dest</code>.
+ * </p>
+ * <p>
+ * Also note that the caller is encouraged to pause an attached {@link GLAnimatorControl}.
+ * </p>
+ * @param src
+ * @param dest
+ * @param listener
+ * @param preserveInitState
+ */
+ public static final void moveGLEventListener(GLAutoDrawable src, GLAutoDrawable dest, GLEventListener listener, boolean preserveInitState) {
+ final boolean initialized = src.getGLEventListenerInitState(listener);
+ src.removeGLEventListener(listener);
+ dest.addGLEventListener(listener);
+ if(preserveInitState && initialized) {
+ dest.setGLEventListenerInitState(listener, true);
+ dest.enqueue(new ReshapeGLEventListener(listener));
+ } // else .. !init state is default
+ }
+
+ /**
+ * Moves all {@link GLEventListener} from {@link GLAutoDrawable} <code>src</code> to <code>dest</code>.
+ * If <code>preserveInitState</code> is <code>true</code>, it's initialized state is preserved
+ * and {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)} issued w/ the next {@link GLAutoDrawable#display()} call.
+ * <p>
+ * Note that it is only legal to pass <code>preserveInitState := true</code>,
+ * if the {@link GLContext} of both <code>src</code> and <code>dest</code> are shared, or has itself moved from <code>src</code> to <code>dest</code>.
+ * </p>
+ * <p>
+ * Also note that the caller is encouraged to pause an attached {@link GLAnimatorControl}.
+ * </p>
+ * @param src
+ * @param dest
+ * @param listener
+ * @param preserveInitState
+ */
+ public static final void moveAllGLEventListener(GLAutoDrawable src, GLAutoDrawable dest, boolean preserveInitState) {
+ for(int count = src.getGLEventListenerCount(); 0<count; count--) {
+ final GLEventListener listener = src.getGLEventListener(0);
+ moveGLEventListener(src, dest, listener, preserveInitState);
+ }
+ }
+
+ /**
+ * Swaps the {@link GLContext} and all {@link GLEventListener} between {@link GLAutoDrawable} <code>a</code> and <code>b</code>,
+ * while preserving it's initialized state, resets the GL-Viewport and issuing {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)}.
+ * <p>
+ * If an {@link GLAnimatorControl} is being attached to {@link GLAutoDrawable} <code>a</code> or <code>b</code>
+ * and the current thread is different than {@link GLAnimatorControl#getThread() the animator's thread}, it is paused during the operation.
+ * </p>
+ * @param a
+ * @param b
+ */
+ public static final void swapGLContextAndAllGLEventListener(GLAutoDrawable a, GLAutoDrawable b) {
+ final GLAnimatorControl aAnim = a.getAnimator();
+ final GLAnimatorControl bAnim = b.getAnimator();
+ final boolean aIsPaused = isAnimatorAnimatingOnOtherThread(aAnim) && aAnim.pause();
+ final boolean bIsPaused = isAnimatorAnimatingOnOtherThread(bAnim) && bAnim.pause();
+
+ // enqueue reset GL-Viewport
+ a.enqueue(setViewport);
+ b.enqueue(setViewport);
+
+ //
+ // cache all GLEventListener and their init-state
+ // enqueue reshape on their destination, if already initialized
+ //
+ final int aSz = a.getGLEventListenerCount();
+ final GLEventListener[] aGLE = new GLEventListener[aSz];
+ final boolean[] aInit = new boolean[aSz];
+ for(int i=0; i<aSz; i++) {
+ final GLEventListener l = a.getGLEventListener(0);
+ final boolean initialized = a.getGLEventListenerInitState(l);
+ aInit[i] = initialized;
+ aGLE[i] = a.removeGLEventListener( l );
+ if( initialized ) {
+ b.enqueue(new ReshapeGLEventListener(l));
+ }
+ }
+ final int bSz = b.getGLEventListenerCount();
+ final GLEventListener[] bGLE = new GLEventListener[bSz];
+ final boolean[] bInit = new boolean[bSz];
+ for(int i=0; i<bSz; i++) {
+ final GLEventListener l = b.getGLEventListener(0);
+ final boolean initialized = b.getGLEventListenerInitState(l);
+ bInit[i] = initialized;
+ bGLE[i] = b.removeGLEventListener( l );
+ if( initialized ) {
+ a.enqueue(new ReshapeGLEventListener(l));
+ }
+ }
+
+ // switch context and trigger reshape
+ b.setContext( a.setContext( b.getContext() ) );
+ a.display();
+ b.display();
+
+ // add all cached GLEventListener to their destination and fix their init-state
+ for(int i=0; i<bSz; i++) {
+ final GLEventListener l = bGLE[i];
+ a.addGLEventListener( l );
+ if( bInit[i] ) {
+ a.setGLEventListenerInitState(l, true);
+ } // else uninitialized is default after add
+ }
+ for(int i=0; i<aSz; i++) {
+ final GLEventListener l = aGLE[i];
+ b.addGLEventListener( l );
+ if( aInit[i] ) {
+ b.setGLEventListenerInitState(l, true);
+ } // else uninitialized is default after add
+ }
+
+ if(aIsPaused) { aAnim.resume(); }
+ if(bIsPaused) { bAnim.resume(); }
+ }
+
+ static GLRunnable setViewport = new GLRunnable() {
+ @Override
+ public boolean run(GLAutoDrawable drawable) {
+ drawable.getGL().glViewport(0, 0, drawable.getWidth(), drawable.getHeight());
+ return true;
+ }
+ };
+
+ private static class ReshapeGLEventListener implements GLRunnable {
+ private GLEventListener listener;
+ ReshapeGLEventListener(GLEventListener listener) {
+ this.listener = listener;
+ }
+ @Override
+ public boolean run(GLAutoDrawable drawable) {
+ listener.reshape(drawable, 0, 0, drawable.getWidth(), drawable.getHeight());
+ return true;
+ }
+ }
+
+ /**
+ * Swaps the {@link GLContext} of given {@link GLAutoDrawable}
+ * and {@link GLAutoDrawable#disposeGLEventListener(GLEventListener, boolean) disposes}
+ * each {@link GLEventListener} w/o removing it.
+ * <p>
+ * The GL-Viewport is reset and {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)} issued implicit.
+ * </p>
+ * <p>
+ * If an {@link GLAnimatorControl} is being attached to GLAutoDrawable src or dest and the current thread is different
+ * than {@link GLAnimatorControl#getThread() the animator's thread}, it is paused during the operation.
+ * </p>
+ * @param src
+ * @param dest
+ */
+ public static final void swapGLContext(GLAutoDrawable src, GLAutoDrawable dest) {
+ final GLAnimatorControl aAnim = src.getAnimator();
+ final GLAnimatorControl bAnim = dest.getAnimator();
+ final boolean aIsPaused = isAnimatorAnimatingOnOtherThread(aAnim) && aAnim.pause();
+ final boolean bIsPaused = isAnimatorAnimatingOnOtherThread(bAnim) && bAnim.pause();
+
+ for(int i = src.getGLEventListenerCount() - 1; 0 <= i; i--) {
+ src.disposeGLEventListener(src.getGLEventListener(i), false);
+ }
+ for(int i = dest.getGLEventListenerCount() - 1; 0 <= i; i--) {
+ dest.disposeGLEventListener(dest.getGLEventListener(i), false);
+ }
+ dest.setContext( src.setContext( dest.getContext() ) );
+
+ src.enqueue(setViewport);
+ dest.enqueue(setViewport);
+ src.display();
+ dest.display();
+
+ if(aIsPaused) { aAnim.resume(); }
+ if(bIsPaused) { bAnim.resume(); }
+ }
+
+}
diff --git a/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java b/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java
index 38f1746f9..fc569b13d 100644
--- a/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java
+++ b/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java
@@ -166,13 +166,26 @@ public interface GLAutoDrawable extends GLDrawable {
*/
public GLContext setContext(GLContext newCtx);
- /** Adds a {@link GLEventListener} to the end of this drawable queue.
- The listeners are notified of events in the order of the queue. */
+ /**
+ * Adds the given {@link GLEventListener listener} to the end of this drawable queue.
+ * The {@link GLEventListener listeners} are notified of events in the order of the queue.
+ * <p>
+ * The newly added listener's {@link GLEventListener#init(GLAutoDrawable) init(..)}
+ * method will be called once before any other of it's callback methods.
+ * See {@link #getGLEventListenerInitState(GLEventListener)} for details.
+ * </p>
+ * @param listener The GLEventListener object to be inserted
+ */
public void addGLEventListener(GLEventListener listener);
/**
- * Adds a {@link GLEventListener} at the given index of this drawable queue.
- * The listeners are notified of events in the order of the queue.
+ * Adds the given {@link GLEventListener listener} at the given index of this drawable queue.
+ * The {@link GLEventListener listeners} are notified of events in the order of the queue.
+ * <p>
+ * The newly added listener's {@link GLEventListener#init(GLAutoDrawable) init(..)}
+ * method will be called once before any other of it's callback methods.
+ * See {@link #getGLEventListenerInitState(GLEventListener)} for details.
+ * </p>
* @param index Position where the listener will be inserted.
* Should be within (0 <= index && index <= size()).
* An index value of -1 is interpreted as the end of the list, size().
@@ -181,30 +194,120 @@ public interface GLAutoDrawable extends GLDrawable {
*/
public void addGLEventListener(int index, GLEventListener listener) throws IndexOutOfBoundsException;
- /**
- * Removes a {@link GLEventListener} from this drawable.
- * Note that if this is done from within a particular drawable's
- * {@link GLEventListener} handler (reshape, display, etc.) that it is not
+ /**
+ * Returns the number of {@link GLEventListener} of this drawable queue.
+ * @return The number of GLEventListener objects of this drawable queue.
+ */
+ public int getGLEventListenerCount();
+
+ /**
+ * Returns the {@link GLEventListener} at the given index of this drawable queue.
+ * @param index Position of the listener to be returned.
+ * Should be within (0 <= index && index < size()).
+ * An index value of -1 is interpreted as last listener, size()-1.
+ * @return The GLEventListener object at the given index.
+ * @throws IndexOutOfBoundsException If the index is not within (0 <= index && index < size()), or -1
+ */
+ public GLEventListener getGLEventListener(int index) throws IndexOutOfBoundsException;
+
+ /**
+ * Retrieves whether the given {@link GLEventListener listener} is initialized or not.
+ * <p>
+ * After {@link #addGLEventListener(GLEventListener) adding} a {@link GLEventListener} it is
+ * marked <i>uninitialized</i> and added to a list of to be initialized {@link GLEventListener}.
+ * If such <i>uninitialized</i> {@link GLEventListener}'s handler methods (reshape, display)
+ * are about to be invoked, it's {@link GLEventListener#init(GLAutoDrawable) init(..)} method is invoked first.
+ * Afterwards the {@link GLEventListener} is marked <i>initialized</i>
+ * and removed from the list of to be initialized {@link GLEventListener}.
+ * </p>
+ * <p>
+ * This methods returns the {@link GLEventListener} initialized state,
+ * i.e. returns <code>false</code> if it is included in the list of to be initialized {@link GLEventListener},
+ * otherwise <code>true</code>.
+ * </p>
+ * @param listener the GLEventListener object to query it's initialized state.
+ */
+ public boolean getGLEventListenerInitState(GLEventListener listener);
+
+ /**
+ * Sets the given {@link GLEventListener listener's} initialized state.
+ * <p>
+ * This methods allows manually setting the {@link GLEventListener} initialized state,
+ * i.e. adding it to, or removing it from the list of to be initialized {@link GLEventListener}.
+ * See {@link #getGLEventListenerInitState(GLEventListener)} for details.
+ * </p>
+ * <p>
+ * <b>Warning:</b> This method does not validate whether the given {@link GLEventListener listener's}
+ * is member of this drawable queue, i.e. {@link #addGLEventListener(GLEventListener) added}.
+ * </p>
+ * <p>
+ * This method is only exposed to allow users full control over the {@link GLEventListener}'s state
+ * and is usually not recommended to change.
+ * </p>
+ * <p>
+ * One use case is moving a {@link GLContext} and their initialized {@link GLEventListener}
+ * from one {@link GLAutoDrawable} to another,
+ * where a subsequent {@link GLEventListener#init(GLAutoDrawable) init(..)} call after adding it
+ * to the new owner is neither required nor desired.
+ * See {@link com.jogamp.opengl.util.GLDrawableUtil#swapGLContextAndAllGLEventListener(GLAutoDrawable, GLAutoDrawable) swapGLContextAndAllGLEventListener(..)}.
+ * </p>
+ * @param listener the GLEventListener object to perform a state change.
+ * @param initialized if <code>true</code>, mark the listener initialized, otherwise uninitialized.
+ */
+ public void setGLEventListenerInitState(GLEventListener listener, boolean initialized);
+
+ /**
+ * Disposes the given {@link GLEventListener listener} via {@link GLEventListener#dispose(GLAutoDrawable) dispose(..)}
+ * if it has been initialized and added to this queue.
+ * <p>
+ * If <code>remove</code> is <code>true</code>, the {@link GLEventListener} is removed from this drawable queue before disposal,
+ * otherwise marked uninitialized.
+ * </p>
+ * <p>
+ * If an {@link GLAnimatorControl} is being attached and the current thread is different
+ * than {@link GLAnimatorControl#getThread() the animator's thread}, it is paused during the operation.
+ * </p>
+ * <p>
+ * Note that this is an expensive operation, since {@link GLEventListener#dispose(GLAutoDrawable) dispose(..)}
+ * is decorated by {@link GLContext#makeCurrent()} and {@link GLContext#release()}.
+ * </p>
+ * <p>
+ * Use {@link #removeGLEventListener(GLEventListener) removeGLEventListener(listener)} instead
+ * if you just want to remove the {@link GLEventListener listener} and <i>don't care</i> about the disposal of the it's (OpenGL) resources.
+ * </p>
+ * <p>
+ * Also note that this is done from within a particular drawable's
+ * {@link GLEventListener} handler (reshape, display, etc.), that it is not
* guaranteed that all other listeners will be evaluated properly
* during this update cycle.
- * @param listener The GLEventListener object to be removed
+ * </p>
+ * @param listener The GLEventListener object to be disposed and removed if <code>remove</code> is <code>true</code>
+ * @param remove pass <code>true</code> to have the <code>listener</code> removed from this drawable queue, otherwise pass <code>false</code>
+ * @return the disposed and/or removed GLEventListener, or null if no action was performed, i.e. listener was not added
*/
- public void removeGLEventListener(GLEventListener listener);
-
+ public GLEventListener disposeGLEventListener(GLEventListener listener, boolean remove);
+
/**
- * Removes a {@link GLEventListener} at the given index from this drawable.
+ * Removes the given {@link GLEventListener listener} from this drawable queue.
+ * <p>
+ * This is an inexpensive operation, since the removed listener's
+ * {@link GLEventListener#dispose(GLAutoDrawable) dispose(..)} method will <i>not</i> be called.
+ * </p>
+ * <p>
+ * Use {@link #disposeGLEventListener(GLEventListener, boolean) disposeGLEventListener(listener, true)}
+ * instead to ensure disposal of the {@link GLEventListener listener}'s (OpenGL) resources.
+ * </p>
+ * <p>
* Note that if this is done from within a particular drawable's
- * {@link GLEventListener} handler (reshape, display, etc.) that it is not
+ * {@link GLEventListener} handler (reshape, display, etc.), that it is not
* guaranteed that all other listeners will be evaluated properly
* during this update cycle.
- * @param index Position of the listener to be removed.
- * Should be within (0 <= index && index < size()).
- * An index value of -1 is interpreted as last listener, size()-1.
- * @return The removed GLEventListener object
- * @throws IndexOutOfBoundsException If the index is not within (0 <= index && index < size()), or -1
+ * </p>
+ * @param listener The GLEventListener object to be removed
+ * @return the removed GLEventListener, or null if listener was not added
*/
- public GLEventListener removeGLEventListener(int index) throws IndexOutOfBoundsException;
-
+ public GLEventListener removeGLEventListener(GLEventListener listener);
+
/**
* <p>
* Registers the usage of an animator, an {@link javax.media.opengl.GLAnimatorControl} implementation.
@@ -236,25 +339,25 @@ public interface GLAutoDrawable extends GLDrawable {
public GLAnimatorControl getAnimator();
/**
- * <p>
* Enqueues a one-shot {@link GLRunnable},
* which will be executed within the next {@link #display()} call
* after all registered {@link GLEventListener}s
* {@link GLEventListener#display(GLAutoDrawable) display(GLAutoDrawable)}
* methods has been called.
- * </p>
* <p>
* If no {@link GLAnimatorControl} is animating (default),<br>
* or if the current thread is the animator thread,<br>
- * a {@link #display()} call is issued after enqueue the <code>GLRunnable</code>.<br>
- * No extra synchronization is performed in case <code>wait</code> is true, since it is executed in the current thread.</p>
+ * a {@link #display()} call is issued after enqueue the <code>GLRunnable</code>,
+ * hence the {@link GLRunnable} will be executed right away.<br/>
+ * </p>
* <p>
- * If an {@link GLAnimatorControl} is animating,<br>
- * no {@link #display()} call is issued, since the animator thread performs it.<br>
+ * If an {@link GLAnimatorControl animator} is running,<br>
+ * no explicit {@link #display()} call is issued, allowing the {@link GLAnimatorControl animator} to perform it when it's due.<br>
* </p>
* <p>
* If <code>wait</code> is <code>true</code> the call blocks until the <code>glRunnable</code>
- * has been executed.<p>
+ * has been executed by the {@link GLAnimatorControl animator}, otherwise the method returns immediately.
+ * </p>
* <p>
* If <code>wait</code> is <code>true</code> <b>and</b>
* {@link #isRealized()} returns <code>false</code> <i>or</i> {@link #getContext()} returns <code>null</code>,
@@ -266,16 +369,40 @@ public interface GLAutoDrawable extends GLDrawable {
* where all blocked callers are being notified.
* </p>
*
- * @param wait if <code>true</code> block until execution of <code>glRunnable</code> is finished, otherwise return immediatly w/o waiting
+ * @param wait if <code>true</code> block until execution of <code>glRunnable</code> is finished, otherwise return immediately w/o waiting
* @param glRunnable the {@link GLRunnable} to execute within {@link #display()}
* @return <code>true</code> if the {@link GLRunnable} has been processed or queued, otherwise <code>false</code>.
*
* @see #setAnimator(GLAnimatorControl)
* @see #display()
* @see GLRunnable
+ * @see #enqueue(GLRunnable)
*/
public boolean invoke(boolean wait, GLRunnable glRunnable);
+ /**
+ * Enqueues a one-shot {@link GLRunnable},
+ * which will be executed within the next {@link #display()} call
+ * after all registered {@link GLEventListener}s
+ * {@link GLEventListener#display(GLAutoDrawable) display(GLAutoDrawable)}
+ * methods has been called.
+ * <p>
+ * Unlike the {@link #invoke(boolean, GLRunnable)}, this method only enqueues the {@link GLRunnable}
+ * w/o taking care of it's execution. Hence either a performing {@link GLAnimatorControl animator}
+ * or explicit user call shall trigger {@link #display()} to ensure it's execution.
+ * </p>
+ * <p>
+ * Method return immediately w/o waiting or blocking
+ * </p>
+ *
+ * @param glRunnable the {@link GLRunnable} to execute within the next {@link #display()} call
+ *
+ * @see #invoke(boolean, GLRunnable)
+ * @see #display()
+ * @see GLRunnable
+ */
+ public void enqueue(GLRunnable glRunnable);
+
/** Destroys all resources associated with this GLAutoDrawable,
inclusive the GLContext.
If a window is attached to it's implementation, it shall be closed.
diff --git a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java
index 1e9fcc95e..5b5f800a4 100644
--- a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java
+++ b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java
@@ -495,7 +495,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
(int) ((getHeight() + bounds.getHeight()) / 2));
return;
}
- if( ! this.helper.isExternalAnimatorAnimating() ) {
+ if( ! this.helper.isAnimatorAnimatingOnOtherThread() ) {
display();
}
}
@@ -699,16 +699,38 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
}
@Override
- public void removeGLEventListener(GLEventListener listener) {
- helper.removeGLEventListener(listener);
+ public int getGLEventListenerCount() {
+ return helper.getGLEventListenerCount();
}
@Override
- public GLEventListener removeGLEventListener(int index) throws IndexOutOfBoundsException {
- return helper.removeGLEventListener(index);
+ public GLEventListener getGLEventListener(int index) throws IndexOutOfBoundsException {
+ return helper.getGLEventListener(index);
+ }
+
+ @Override
+ public boolean getGLEventListenerInitState(GLEventListener listener) {
+ return helper.getGLEventListenerInitState(listener);
+ }
+
+ @Override
+ public void setGLEventListenerInitState(GLEventListener listener, boolean initialized) {
+ helper.setGLEventListenerInitState(listener, initialized);
}
@Override
+ public GLEventListener disposeGLEventListener(GLEventListener listener, boolean remove) {
+ final DisposeGLEventListenerAction r = new DisposeGLEventListenerAction(listener, remove);
+ Threading.invoke(true, r, getTreeLock());
+ return r.listener;
+ }
+
+ @Override
+ public GLEventListener removeGLEventListener(GLEventListener listener) {
+ return helper.removeGLEventListener(listener);
+ }
+
+ @Override
public void setAnimator(GLAnimatorControl animatorControl) {
helper.setAnimator(animatorControl);
}
@@ -724,6 +746,11 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
}
@Override
+ public void enqueue(GLRunnable glRunnable) {
+ helper.enqueue(glRunnable);
+ }
+
+ @Override
public GLContext setContext(GLContext newCtx) {
final RecursiveLock _lock = lock;
_lock.lock();
@@ -984,15 +1011,12 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
}
};
- // Workaround for ATI driver bugs related to multithreading issues
- // like simultaneous rendering via Animators to canvases that are
- // being resized on the AWT event dispatch thread
private final Runnable displayOnEDTAction = new Runnable() {
@Override
public void run() {
final RecursiveLock _lock = lock;
_lock.lock();
- try {
+ try {
helper.invokeGL(drawable, context, displayAction, initAction);
} finally {
_lock.unlock();
@@ -1015,6 +1039,26 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
}
};
+ private class DisposeGLEventListenerAction implements Runnable {
+ GLEventListener listener;
+ private boolean remove;
+ private DisposeGLEventListenerAction(GLEventListener listener, boolean remove) {
+ this.listener = listener;
+ this.remove = remove;
+ }
+
+ @Override
+ public void run() {
+ final RecursiveLock _lock = lock;
+ _lock.lock();
+ try {
+ listener = helper.disposeGLEventListener(GLCanvas.this, drawable, context, listener, remove);
+ } finally {
+ _lock.unlock();
+ }
+ }
+ };
+
// Disables the AWT's erasing of this Canvas's background on Windows
// in Java SE 6. This internal API is not available in previous
// releases, but the system property
diff --git a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java
index d0b9fb913..08d70211b 100644
--- a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java
+++ b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java
@@ -426,16 +426,48 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
}
@Override
- public void removeGLEventListener(GLEventListener listener) {
- helper.removeGLEventListener(listener);
+ public int getGLEventListenerCount() {
+ return helper.getGLEventListenerCount();
}
@Override
- public GLEventListener removeGLEventListener(int index) throws IndexOutOfBoundsException {
- return helper.removeGLEventListener(index);
- }
+ public GLEventListener getGLEventListener(int index) throws IndexOutOfBoundsException {
+ return helper.getGLEventListener(index);
+ }
+
+ @Override
+ public boolean getGLEventListenerInitState(GLEventListener listener) {
+ return helper.getGLEventListenerInitState(listener);
+ }
+
+ @Override
+ public void setGLEventListenerInitState(GLEventListener listener, boolean initialized) {
+ helper.setGLEventListenerInitState(listener, initialized);
+ }
+
+ @Override
+ public GLEventListener disposeGLEventListener(GLEventListener listener, boolean remove) {
+ final DisposeGLEventListenerAction r = new DisposeGLEventListenerAction(listener, remove);
+ if (EventQueue.isDispatchThread()) {
+ r.run();
+ } else {
+ // Multithreaded redrawing of Swing components is not allowed,
+ // so do everything on the event dispatch thread
+ try {
+ EventQueue.invokeAndWait(r);
+ } catch (Exception e) {
+ throw new GLException(e);
+ }
+ }
+ return r.listener;
+ }
@Override
+ public GLEventListener removeGLEventListener(GLEventListener listener) {
+ return helper.removeGLEventListener(listener);
+ }
+
+ @Override
public void setAnimator(GLAnimatorControl animatorControl) {
helper.setAnimator(animatorControl);
}
@@ -451,6 +483,11 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
}
@Override
+ public void enqueue(GLRunnable glRunnable) {
+ helper.enqueue(glRunnable);
+ }
+
+ @Override
public GLContext createContext(GLContext shareWith) {
return (null != backend) ? backend.createContext(shareWith) : null;
}
@@ -685,7 +722,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
@Override
public void dispose(GLAutoDrawable drawable) {
- helper.dispose(GLJPanel.this);
+ helper.disposeAllGLEventListener(GLJPanel.this, false);
}
@Override
@@ -758,6 +795,20 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
}
};
+ private class DisposeGLEventListenerAction implements Runnable {
+ GLEventListener listener;
+ private boolean remove;
+ private DisposeGLEventListenerAction(GLEventListener listener, boolean remove) {
+ this.listener = listener;
+ this.remove = remove;
+ }
+
+ @Override
+ public void run() {
+ listener = helper.disposeGLEventListener(GLJPanel.this, backend.getDrawable(), backend.getContext(), listener, remove);
+ }
+ };
+
private int getNextPowerOf2(int number) {
// Workaround for problems where 0 width or height are transiently
// seen during layout
diff --git a/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java b/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java
index 85156e8a1..09a754279 100644
--- a/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java
+++ b/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java
@@ -95,7 +95,7 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, FPSCounter {
protected final void defaultWindowRepaintOp() {
final GLDrawable _drawable = drawable;
if( null != _drawable && _drawable.isRealized() ) {
- if( !_drawable.getNativeSurface().isSurfaceLockedByOtherThread() && !helper.isExternalAnimatorAnimating() ) {
+ if( !_drawable.getNativeSurface().isSurfaceLockedByOtherThread() && !helper.isAnimatorAnimatingOnOtherThread() ) {
display();
}
}
@@ -124,7 +124,7 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, FPSCounter {
}
sendReshape = true; // async if display() doesn't get called below, but avoiding deadlock
if( _drawable.isRealized() ) {
- if( !_drawable.getNativeSurface().isSurfaceLockedByOtherThread() && !helper.isExternalAnimatorAnimating() ) {
+ if( !_drawable.getNativeSurface().isSurfaceLockedByOtherThread() && !helper.isAnimatorAnimatingOnOtherThread() ) {
display();
}
}
@@ -178,7 +178,7 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, FPSCounter {
final GLAnimatorControl ctrl = helper.getAnimator();
// Is an animator thread perform rendering?
- if ( helper.isAnimatorRunningOnOtherThread() ) {
+ if ( helper.isAnimatorStartedOnOtherThread() ) {
// Pause animations before initiating safe destroy.
final boolean isPaused = ctrl.pause();
destroy();
@@ -299,6 +299,16 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, FPSCounter {
}
}
+ protected final GLEventListener defaultDisposeGLEventListener(GLEventListener listener, boolean remove) {
+ final RecursiveLock _lock = getLock();
+ _lock.lock();
+ try {
+ return helper.disposeGLEventListener(GLAutoDrawableBase.this, drawable, context, listener, remove);
+ } finally {
+ _lock.unlock();
+ }
+ }
+
@Override
public final GLDrawable getDelegatedDrawable() {
return drawable;
@@ -356,13 +366,33 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, FPSCounter {
}
@Override
- public final void removeGLEventListener(GLEventListener listener) {
- helper.removeGLEventListener(listener);
+ public int getGLEventListenerCount() {
+ return helper.getGLEventListenerCount();
+ }
+
+ @Override
+ public GLEventListener getGLEventListener(int index) throws IndexOutOfBoundsException {
+ return helper.getGLEventListener(index);
+ }
+
+ @Override
+ public boolean getGLEventListenerInitState(GLEventListener listener) {
+ return helper.getGLEventListenerInitState(listener);
+ }
+
+ @Override
+ public void setGLEventListenerInitState(GLEventListener listener, boolean initialized) {
+ helper.setGLEventListenerInitState(listener, initialized);
+ }
+
+ @Override
+ public GLEventListener disposeGLEventListener(GLEventListener listener, boolean remove) {
+ return defaultDisposeGLEventListener(listener, remove);
}
@Override
- public GLEventListener removeGLEventListener(int index) throws IndexOutOfBoundsException {
- return helper.removeGLEventListener(index);
+ public final GLEventListener removeGLEventListener(GLEventListener listener) {
+ return helper.removeGLEventListener(listener);
}
@Override
@@ -382,6 +412,11 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, FPSCounter {
}
@Override
+ public final void enqueue(GLRunnable glRunnable) {
+ helper.enqueue(glRunnable);
+ }
+
+ @Override
public final void setAutoSwapBufferMode(boolean enable) {
helper.setAutoSwapBufferMode(enable);
}
diff --git a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java
index d4ff9702c..d891ae810 100644
--- a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java
+++ b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java
@@ -61,7 +61,6 @@ import com.jogamp.opengl.util.Animator;
/** Encapsulates the implementation of most of the GLAutoDrawable's
methods to be able to share it between GLCanvas and GLJPanel. */
-
public class GLDrawableHelper {
/** true if property <code>jogl.debug.GLDrawable.PerfStats</code> is defined. */
private static final boolean PERF_STATS = Debug.isPropertyDefined("jogl.debug.GLDrawable.PerfStats", true);
@@ -75,6 +74,7 @@ public class GLDrawableHelper {
private boolean autoSwapBufferMode;
private Thread skipContextReleaseThread;
private GLAnimatorControl animatorCtrl;
+ private static Runnable nop = new Runnable() { public void run() {} };
public GLDrawableHelper() {
reset();
@@ -289,45 +289,239 @@ public class GLDrawableHelper {
// GLEventListener may be added after context is created,
// hence we earmark initialization for the next display call.
listenersToBeInit.add(listener);
+
listeners.add(index, listener);
}
}
-
- public final void removeGLEventListener(GLEventListener listener) {
+
+ /**
+ * Note that no {@link GLEventListener#dispose(GLAutoDrawable)} call is being issued
+ * due to the lack of a current context.
+ * Consider calling {@link #disposeGLEventListener(GLAutoDrawable, GLDrawable, GLContext, GLEventListener)}.
+ * @return the removed listener, or null if listener was not added
+ */
+ public final GLEventListener removeGLEventListener(GLEventListener listener) {
synchronized(listenersLock) {
- listeners.remove(listener);
listenersToBeInit.remove(listener);
+ return listeners.remove(listener) ? listener : null;
}
}
- public final GLEventListener removeGLEventListener(int index)
- throws IndexOutOfBoundsException {
+ public final GLEventListener removeGLEventListener(int index) throws IndexOutOfBoundsException {
synchronized(listenersLock) {
if(0>index) {
index = listeners.size()-1;
}
- return listeners.remove(index);
+ final GLEventListener listener = listeners.remove(index);
+ listenersToBeInit.remove(listener);
+ return listener;
+ }
+ }
+
+ public final int getGLEventListenerCount() {
+ synchronized(listenersLock) {
+ return listeners.size();
+ }
+ }
+
+ public final GLEventListener getGLEventListener(int index) throws IndexOutOfBoundsException {
+ synchronized(listenersLock) {
+ if(0>index) {
+ index = listeners.size()-1;
+ }
+ return listeners.get(index);
+ }
+ }
+
+ public final boolean getGLEventListenerInitState(GLEventListener listener) {
+ synchronized(listenersLock) {
+ return !listenersToBeInit.contains(listener);
+ }
+ }
+
+ public final void setGLEventListenerInitState(GLEventListener listener, boolean initialized) {
+ synchronized(listenersLock) {
+ if(initialized) {
+ listenersToBeInit.remove(listener);
+ } else {
+ listenersToBeInit.add(listener);
+ }
}
}
/**
- * Issues {@link javax.media.opengl.GLEventListener#dispose(javax.media.opengl.GLAutoDrawable)}
- * to all listeners.
+ * Disposes the given {@link GLEventListener} via {@link GLEventListener#dispose(GLAutoDrawable)}
+ * if it has been initialized and added to this queue.
* <p>
- * Please consider using {@link #disposeGL(GLAutoDrawable, GLDrawable, GLContext, Runnable)}
- * for correctness!
+ * If <code>remove</code> is <code>true</code>, the {@link GLEventListener} is removed from this drawable queue before disposal,
+ * otherwise marked uninitialized.
* </p>
- * @param drawable
+ * <p>
+ * Please consider using {@link #disposeGLEventListener(GLAutoDrawable, GLDrawable, GLContext, GLEventListener)}
+ * for correctness, i.e. encapsulating all calls w/ makeCurrent etc.
+ * </p>
+ * @param autoDrawable
+ * @param remove if true, the listener gets removed
+ * @return the disposed and/or removed listener, otherwise null if neither action is performed
*/
- public final void dispose(GLAutoDrawable drawable) {
+ public final GLEventListener disposeGLEventListener(GLAutoDrawable autoDrawable, GLEventListener listener, boolean remove) {
+ synchronized(listenersLock) {
+ if( remove ) {
+ if( listeners.remove(listener) ) {
+ if( !listenersToBeInit.remove(listener) ) {
+ listener.dispose(autoDrawable);
+ }
+ return listener;
+ }
+ } else {
+ if( listeners.contains(listener) && !listenersToBeInit.contains(listener) ) {
+ listener.dispose(autoDrawable);
+ listenersToBeInit.add(listener);
+ return listener;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Disposes all added initialized {@link GLEventListener}s via {@link GLEventListener#dispose(GLAutoDrawable)}.
+ * <p>
+ * If <code>remove</code> is <code>true</code>, the {@link GLEventListener}s are removed from this drawable queue before disposal,
+ * otherwise maked uninitialized.
+ * </p>
+ * <p>
+ * Please consider using {@link #disposeAllGLEventListener(GLAutoDrawable, GLDrawable, GLContext)}
+ * or {@link #disposeGL(GLAutoDrawable, GLDrawable, GLContext, Runnable)}
+ * for correctness, i.e. encapsulating all calls w/ makeCurrent etc.
+ * </p>
+ * @param autoDrawable
+ * @return the disposal count
+ */
+ public final int disposeAllGLEventListener(GLAutoDrawable autoDrawable, boolean remove) {
+ int disposeCount = 0;
synchronized(listenersLock) {
- final ArrayList<GLEventListener> _listeners = listeners;
- for (int i=0; i < _listeners.size(); i++) {
- _listeners.get(i).dispose(drawable);
+ if( remove ) {
+ for (int count = listeners.size(); 0 < count; count--) {
+ final GLEventListener listener = listeners.remove(0);
+ if( !listenersToBeInit.remove(listener) ) {
+ listener.dispose(autoDrawable);
+ disposeCount++;
+ }
+ }
+ } else {
+ final int count = listeners.size();
+ for (int i = 0; i < count; i++) {
+ final GLEventListener listener = listeners.get(i);
+ if( !listenersToBeInit.contains(listener) ) {
+ listener.dispose(autoDrawable);
+ listenersToBeInit.add(listener);
+ disposeCount++;
+ }
+ }
}
}
+ return disposeCount;
}
+ /**
+ * Principal helper method which runs {@link #disposeGLEventListener(GLAutoDrawable, GLEventListener, boolean)}
+ * with the context made current.
+ * <p>
+ * If an {@link GLAnimatorControl} is being attached and the current thread is different
+ * than {@link GLAnimatorControl#getThread() the animator's thread}, it is paused during the operation.
+ * </p>
+ *
+ * @param autoDrawable
+ * @param context
+ * @param listener
+ * @param initAction
+ */
+ public final GLEventListener disposeGLEventListener(final GLAutoDrawable autoDrawable,
+ final GLDrawable drawable,
+ final GLContext context,
+ final GLEventListener listener,
+ final boolean remove) {
+ synchronized(listenersLock) {
+ // fast path for uninitialized listener
+ if( listenersToBeInit.contains(listener) ) {
+ if( remove ) {
+ listenersToBeInit.remove(listener);
+ return listeners.remove(listener) ? listener : null;
+ }
+ return null;
+ }
+ }
+ final boolean isPaused = isAnimatorAnimatingOnOtherThread() && animatorCtrl.pause();
+ final GLEventListener[] res = new GLEventListener[] { null };
+ final Runnable action = new Runnable() {
+ public void run() {
+ res[0] = disposeGLEventListener(autoDrawable, listener, remove);
+ }
+ };
+ invokeGL(drawable, context, action, nop);
+
+ if(isPaused) {
+ animatorCtrl.resume();
+ }
+ return res[0];
+ }
+
+ /**
+ * Principal helper method which runs {@link #disposeAllGLEventListener(GLAutoDrawable, boolean)}
+ * with the context made current.
+ * <p>
+ * If an {@link GLAnimatorControl} is being attached and the current thread is different
+ * than {@link GLAnimatorControl#getThread() the animator's thread}, it is paused during the operation.
+ * </p>
+ *
+ * @param autoDrawable
+ * @param context
+ * @param listener
+ * @param initAction
+ */
+ public final void disposeAllGLEventListener(final GLAutoDrawable autoDrawable,
+ final GLDrawable drawable,
+ final GLContext context,
+ final boolean remove) {
+
+ final boolean isPaused = isAnimatorAnimatingOnOtherThread() && animatorCtrl.pause();
+
+ final Runnable action = new Runnable() {
+ public void run() {
+ disposeAllGLEventListener(autoDrawable, remove);
+ }
+ };
+ invokeGL(drawable, context, action, nop);
+
+ if(isPaused) {
+ animatorCtrl.resume();
+ }
+ }
+
+ /**
+ * Principal helper method which runs
+ * {@link #disposeAllGLEventListener(GLAutoDrawable, boolean) disposeAllGLEventListener(autoDrawable, false)}
+ * with the context made current <b>and</b> destroys the context afterwards while holding the lock.
+ * @param autoDrawable
+ * @param drawable
+ * @param context
+ * @param postAction
+ */
+ public final void disposeGL(final GLAutoDrawable autoDrawable,
+ final GLDrawable drawable,
+ final GLContext context,
+ final Runnable postAction) {
+ if(PERF_STATS) {
+ invokeGLImplStats(drawable, context, null, null, autoDrawable);
+ } else {
+ invokeGLImpl(drawable, context, null, null, autoDrawable);
+ }
+ if(null != postAction) {
+ postAction.run();
+ }
+ }
+
private final void init(GLEventListener l, GLAutoDrawable drawable, boolean sendReshape) {
l.init(drawable);
if(sendReshape) {
@@ -374,7 +568,7 @@ public class GLDrawableHelper {
}
}
}
-
+
private final void reshape(GLEventListener listener, GLAutoDrawable drawable,
int x, int y, int width, int height, boolean setViewport, boolean checkInit) {
if(checkInit) {
@@ -459,15 +653,15 @@ public class GLDrawableHelper {
}
}
- public final boolean isAnimatorRunningOnOtherThread() {
+ public final boolean isAnimatorStartedOnOtherThread() {
return ( null != animatorCtrl ) ? animatorCtrl.isStarted() && animatorCtrl.getThread() != Thread.currentThread() : false ;
}
- public final boolean isAnimatorRunning() {
+ public final boolean isAnimatorStarted() {
return ( null != animatorCtrl ) ? animatorCtrl.isStarted() : false ;
}
- public final boolean isExternalAnimatorAnimating() {
+ public final boolean isAnimatorAnimatingOnOtherThread() {
return ( null != animatorCtrl ) ? animatorCtrl.isAnimating() && animatorCtrl.getThread() != Thread.currentThread() : false ;
}
@@ -501,9 +695,9 @@ public class GLDrawableHelper {
GLRunnableTask rTask = null;
Object rTaskLock = new Object();
synchronized(rTaskLock) {
- boolean deferred;
+ final boolean deferred;
synchronized(glRunnablesLock) {
- deferred = isExternalAnimatorAnimating();
+ deferred = isAnimatorAnimatingOnOtherThread();
if(!deferred) {
wait = false; // don't wait if exec immediatly
}
@@ -531,6 +725,15 @@ public class GLDrawableHelper {
return true;
}
+ public final void enqueue(GLRunnable glRunnable) {
+ if( null == glRunnable) {
+ return;
+ }
+ synchronized(glRunnablesLock) {
+ glRunnables.add( new GLRunnableTask(glRunnable, null, false) );
+ }
+ }
+
public final void setAutoSwapBufferMode(boolean enable) {
autoSwapBufferMode = enable;
}
@@ -576,10 +779,10 @@ public class GLDrawableHelper {
* @param runnable
* @param initAction
*/
- public final void invokeGL(GLDrawable drawable,
- GLContext context,
- Runnable runnable,
- Runnable initAction) {
+ public final void invokeGL(final GLDrawable drawable,
+ final GLContext context,
+ final Runnable runnable,
+ final Runnable initAction) {
if(null==context) {
if (DEBUG) {
Exception e = new GLException(Thread.currentThread().getName()+" Info: GLDrawableHelper " + this + ".invokeGL(): NULL GLContext");
@@ -595,34 +798,11 @@ public class GLDrawableHelper {
}
}
- /**
- * Principal helper method which runs {@link #dispose(GLAutoDrawable)} with the context
- * made current and destroys the context afterwards while holding the lock.
- *
- * @param autoDrawable
- * @param drawable
- * @param context
- * @param postAction
- */
- public final void disposeGL(GLAutoDrawable autoDrawable,
- GLDrawable drawable,
- GLContext context,
- Runnable postAction) {
- if(PERF_STATS) {
- invokeGLImplStats(drawable, context, null, null, autoDrawable);
- } else {
- invokeGLImpl(drawable, context, null, null, autoDrawable);
- }
- if(null != postAction) {
- postAction.run();
- }
- }
-
- private final void invokeGLImpl(GLDrawable drawable,
- GLContext context,
- Runnable runnable,
- Runnable initAction,
- GLAutoDrawable disposeAutoDrawable) {
+ private final void invokeGLImpl(final GLDrawable drawable,
+ final GLContext context,
+ final Runnable runnable,
+ final Runnable initAction,
+ final GLAutoDrawable disposeAutoDrawable) {
final Thread currentThread = Thread.currentThread();
final boolean isDisposeAction = null==initAction ;
@@ -660,8 +840,8 @@ public class GLDrawableHelper {
if(GLContext.CONTEXT_CURRENT_NEW == res) {
throw new GLException(currentThread.getName()+" GLDrawableHelper " + this + ".invokeGL(): Dispose case (no init action given): Native context was not created (new ctx): "+context);
}
- if(listeners.size()>0) {
- dispose(disposeAutoDrawable);
+ if( listeners.size() > 0 && null != disposeAutoDrawable ) {
+ disposeAllGLEventListener(disposeAutoDrawable, false);
}
}
}
@@ -686,11 +866,11 @@ public class GLDrawableHelper {
}
}
- private final void invokeGLImplStats(GLDrawable drawable,
- GLContext context,
- Runnable runnable,
- Runnable initAction,
- GLAutoDrawable disposeAutoDrawable) {
+ private final void invokeGLImplStats(final GLDrawable drawable,
+ final GLContext context,
+ final Runnable runnable,
+ final Runnable initAction,
+ final GLAutoDrawable disposeAutoDrawable) {
final Thread currentThread = Thread.currentThread();
final boolean isDisposeAction = null==initAction ;
@@ -748,8 +928,8 @@ public class GLDrawableHelper {
if(res == GLContext.CONTEXT_CURRENT_NEW) {
throw new GLException(currentThread.getName()+" GLDrawableHelper " + this + ".invokeGL(): Dispose case (no init action given): Native context was not created (new ctx): "+context);
}
- if(listeners.size()>0) {
- dispose(disposeAutoDrawable);
+ if( listeners.size() > 0 && null != disposeAutoDrawable ) {
+ disposeAllGLEventListener(disposeAutoDrawable, false);
}
}
}
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextDrawableSwitchNEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextDrawableSwitchNEWT.java
index 496be3d93..8d4b8f700 100644
--- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextDrawableSwitchNEWT.java
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextDrawableSwitchNEWT.java
@@ -43,11 +43,11 @@ import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLContext;
import javax.media.opengl.GLDrawable;
import javax.media.opengl.GLDrawableFactory;
-import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLProfile;
import com.jogamp.opengl.GLAutoDrawableDelegate;
import com.jogamp.opengl.util.Animator;
+import com.jogamp.opengl.util.GLDrawableUtil;
import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2;
import com.jogamp.opengl.test.junit.jogl.demos.es2.RedSquareES2;
@@ -157,28 +157,11 @@ public class TestGLContextDrawableSwitchNEWT extends UITestCase {
if( ( t1 - t0 ) / period > s) {
s++;
System.err.println(s+" - switch - START "+ ( t1 - t0 ));
- animator.pause();
// switch context _and_ the demo synchronously
- if(0 == s%2) {
- final GLEventListener demo = glad2.removeGLEventListener(0);
- GLContext ctx1 = glad1.setContext(glad2.getContext());
- glad2.setContext(ctx1);
- glad1.addGLEventListener(0, demo);
- } else {
- final GLEventListener demo = glad1.removeGLEventListener(0);
- GLContext ctx2 = glad2.setContext(glad1.getContext());
- glad1.setContext(ctx2);
- glad2.addGLEventListener(0, demo);
- }
- System.err.println(s+" - switch - display-1");
- glad1.display();
- System.err.println(s+" - switch - display-2");
- glad2.display();
+ GLDrawableUtil.swapGLContextAndAllGLEventListener(glad1, glad2);
- System.err.println(s+" - switch - END "+ ( t1 - t0 ));
-
- animator.resume();
+ System.err.println(s+" - switch - END "+ ( t1 - t0 ));
}
Thread.sleep(100);
t1 = System.currentTimeMillis();
@@ -223,24 +206,15 @@ public class TestGLContextDrawableSwitchNEWT extends UITestCase {
if( ( t1 - t0 ) / period > s) {
s++;
System.err.println(s+" - switch - START "+ ( t1 - t0 ));
- animator.pause();
+ System.err.println(s+" - A w1-h 0x"+Long.toHexString(glWindow1.getHandle())+",-ctx 0x"+Long.toHexString(glWindow1.getContext().getHandle()));
+ System.err.println(s+" - A w2-h 0x"+Long.toHexString(glWindow2.getHandle())+",-ctx 0x"+Long.toHexString(glWindow2.getContext().getHandle()));
// switch context _and_ the demo synchronously
- if(0 == s%2) {
- final GLEventListener demo = glWindow2.removeGLEventListener(0);
- GLContext ctx1 = glWindow1.setContext(glWindow2.getContext());
- glWindow1.addGLEventListener(0, demo);
- glWindow2.setContext(ctx1);
- } else {
- final GLEventListener demo = glWindow1.removeGLEventListener(0);
- GLContext ctx2 = glWindow2.setContext(glWindow1.getContext());
- glWindow2.addGLEventListener(0, demo);
- glWindow1.setContext(ctx2);
- }
+ GLDrawableUtil.swapGLContextAndAllGLEventListener(glWindow1, glWindow2);
+ System.err.println(s+" - B w1-h 0x"+Long.toHexString(glWindow1.getHandle())+",-ctx 0x"+Long.toHexString(glWindow1.getContext().getHandle()));
+ System.err.println(s+" - B w2-h 0x"+Long.toHexString(glWindow2.getHandle())+",-ctx 0x"+Long.toHexString(glWindow2.getContext().getHandle()));
System.err.println(s+" - switch - END "+ ( t1 - t0 ));
-
- animator.resume();
}
Thread.sleep(100);
t1 = System.currentTimeMillis();
@@ -288,27 +262,23 @@ public class TestGLContextDrawableSwitchNEWT extends UITestCase {
if( ( t1 - t0 ) / period > s) {
s++;
System.err.println(s+" - switch - START "+ ( t1 - t0 ));
- animator.pause();
-
- GLEventListener demo1 = glWindow1.removeGLEventListener(0);
- GLEventListener demo2 = glWindow2.removeGLEventListener(0);
-
- GLContext ctx1 = glWindow1.setContext(glWindow2.getContext());
- glWindow1.addGLEventListener(0, demo2);
-
- glWindow2.setContext(ctx1);
- glWindow2.addGLEventListener(0, demo1);
-
+ System.err.println(s+" - A w1-h 0x"+Long.toHexString(glWindow1.getHandle())+",-ctx 0x"+Long.toHexString(glWindow1.getContext().getHandle()));
+ System.err.println(s+" - A w2-h 0x"+Long.toHexString(glWindow2.getHandle())+",-ctx 0x"+Long.toHexString(glWindow2.getContext().getHandle()));
+ GLDrawableUtil.swapGLContextAndAllGLEventListener(glWindow1, glWindow2);
+ System.err.println(s+" - B w1-h 0x"+Long.toHexString(glWindow1.getHandle())+",-ctx 0x"+Long.toHexString(glWindow1.getContext().getHandle()));
+ System.err.println(s+" - B w2-h 0x"+Long.toHexString(glWindow2.getHandle())+",-ctx 0x"+Long.toHexString(glWindow2.getContext().getHandle()));
System.err.println(s+" - switch - END "+ ( t1 - t0 ));
-
- animator.resume();
}
Thread.sleep(100);
t1 = System.currentTimeMillis();
}
animator.stop();
+ System.err.println("pre -del-w1: w1: "+glWindow1);
+ System.err.println("pre -del-w1: w2: "+glWindow2);
glWindow1.destroy();
+ System.err.println("post-del-w1: w1: "+glWindow1);
+ System.err.println("post-del-w1: w2: "+glWindow2);
glWindow2.destroy();
}
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java
index e703b6fd9..e9fe9b401 100644
--- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java
@@ -65,7 +65,6 @@ public class GearsES2 implements GLEventListener {
private int prevMouseX, prevMouseY;
private boolean doRotate = true;
- private boolean isInitialized = false;
boolean ignoreFocus = false;
public GearsES2(int swapInterval) {
@@ -106,11 +105,6 @@ public class GearsES2 implements GLEventListener {
public void init(GLAutoDrawable drawable) {
- if(isInitialized) {
- System.err.println(Thread.currentThread()+" GearsES2.init skipped!");
- return;
- }
- isInitialized = true;
System.err.println(Thread.currentThread()+" GearsES2.init ...");
GL2ES2 gl = drawable.getGL().getGL2ES2();
@@ -228,11 +222,6 @@ public class GearsES2 implements GLEventListener {
// private boolean useAndroidDebug = false;
public void dispose(GLAutoDrawable drawable) {
- if(!isInitialized) {
- System.err.println(Thread.currentThread()+" GearsES2.dispose skipped!");
- return;
- }
- isInitialized = false;
System.err.println(Thread.currentThread()+" GearsES2.dispose ... ");
final Object upstreamWidget = drawable.getUpstreamWidget();
if (upstreamWidget instanceof Window) {
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/RedSquareES2.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/RedSquareES2.java
index bf1ca5c2d..e4a457954 100644
--- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/RedSquareES2.java
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/RedSquareES2.java
@@ -50,7 +50,6 @@ public class RedSquareES2 implements GLEventListener {
Window window = null;
float aspect = 1.0f;
boolean doRotate = true;
- boolean isInitialized = false;
public RedSquareES2(int swapInterval) {
this.swapInterval = swapInterval;
@@ -64,11 +63,6 @@ public class RedSquareES2 implements GLEventListener {
public void setDoRotation(boolean rotate) { this.doRotate = rotate; }
public void init(GLAutoDrawable glad) {
- if(isInitialized) {
- System.err.println(Thread.currentThread()+" RedSquareES2.init skipped!");
- return;
- }
- isInitialized = true;
System.err.println(Thread.currentThread()+" RedSquareES2.init ...");
GL2ES2 gl = glad.getGL().getGL2ES2();
@@ -184,11 +178,6 @@ public class RedSquareES2 implements GLEventListener {
}
public void dispose(GLAutoDrawable glad) {
- if(!isInitialized) {
- System.err.println(Thread.currentThread()+" RedSquareES2.dispose skipped!");
- return;
- }
- isInitialized = false;
System.err.println(Thread.currentThread()+" RedSquareES2.dispose ... ");
GL2ES2 gl = glad.getGL().getGL2ES2();
st.destroy(gl);
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/TextureSequenceCubeES2.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/TextureSequenceCubeES2.java
index 9217e2b53..25f8740d4 100644
--- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/TextureSequenceCubeES2.java
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/TextureSequenceCubeES2.java
@@ -75,7 +75,6 @@ public class TextureSequenceCubeES2 implements GLEventListener {
private float view_rotx = 0.0f, view_roty = 0.0f, view_rotz = 0.0f;
int[] vboNames = new int[4];
boolean innerCube;
- boolean initialized = false;
private ByteBuffer cubeIndices;
private final MouseListener mouseAction = new MouseAdapter() {