diff options
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() { |