aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2012-11-04 07:09:39 +0100
committerSven Gothel <[email protected]>2012-11-04 07:09:39 +0100
commitc002e04f848116922a1ed7bd96ead54961649bbd (patch)
tree37d51d971aceabb3f1afe8ce3cd7164a0096842b /src
parent808c8da0729b845d010d3f5e0babf1fc6129c3e9 (diff)
GLAutoDrawable: Fix GLEventListener lifecycle and expose more user control (API Change) ; Added GLDrawableUtil
A GLEventListener resides in two states, initialized and uninitialized. When added to a GLAutoDrawable, it is uninitialized. A first 'display()' will issue GLEventListener's 'init(..)' which renders it initialized. This is usually accompanied by 'reshape(..)' propagating the drawable's dimension. Destruction of the GLAutoDrawable will issue GLEventListener's 'dispose(..)' which renders it uninitialized. It turns our these means of GLEventListener controls are not sufficient in case the user requires to remove and add them during the lifecycle and rendering of their GLAutoDrawable host. GLAutoDrawable 'removeGLEventListener(..)' merely removes the GLEventListener from the list, but does not complete it's lifecycle, i.e. issues 'dispose(..)' if initialized to realease GL related resources. Hence the following essential API changes are made to complete the lifecycle: + public GLEventListener disposeGLEventListener(GLEventListener listener, boolean remove); disposing a single GLEventListener, allowing it's removal from the list being optional This is demonstrated via GLDrawableUtil.swapGLContextAndAllGLEventListener(GLAutoDrawable a, GLAutoDrawable b), see below. ++++++++ Further more the following API changes were made to expose complete control of GLEventListener to the user: - public void removeGLEventListener(GLEventListener listener); + public GLEventListener removeGLEventListener(GLEventListener listener); The return value allows simple pipelining, and also delivers information whether the passed listener was actually removed. - public GLEventListener removeGLEventListener(int index) throws IndexOutOfBoundsException; + public int getGLEventListenerCount(); + public GLEventListener getGLEventListener(int index) throws IndexOutOfBoundsException; Dropping the redundant removal by index, while adding count and get methods. + public boolean getGLEventListenerInitState(GLEventListener listener); + public void setGLEventListenerInitState(GLEventListener listener, boolean initialized); Allows retrieving and setting of listener states. All in all these API changes allows a user to experience all freedoms in dealing w/ GLEventListeners hosted by GLAutoDrawable impl. and shall be future proof. Note that we have avoided the Iterator pattern due to it's overhead of temporal objects creation. The simple indexed access allows us to implement each method as an atomic operation. +++++++++++ Further more a simple enqueue(..) method has been added, allowing to just enqueue a GLRunnable w/o provoking it's execution - as invoke(..) does. This method pleases a use case where GLRunnables are batched and shall be executed later on.. public boolean invoke(boolean wait, GLRunnable glRunnable); + public void enqueue(GLRunnable glRunnable); +++++++++++ Added GLDrawableUtil, exposes utility function to rearrange GLEventListener, modifiy GLAutoDrawable, etc. GLDrawableUtil.swapGLContextAndAllGLEventListener(GLAutoDrawable a, GLAutoDrawable b) is tested and demonstrated w/ TestGLContextDrawableSwitchNEWT. Manually tested on X11, OSX and Windows.
Diffstat (limited to 'src')
-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
11 files changed, 868 insertions, 196 deletions
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() {