diff options
author | Sven Gothel <[email protected]> | 2013-03-13 06:35:30 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2013-03-13 06:35:30 +0100 |
commit | c225285e09f0a29fca418601bf1aa07cafb54339 (patch) | |
tree | 5cf619ad3b51db76511c3809d32ef29156eb71dd | |
parent | 8457bf35fee253d9af29ff1150a9671f6896fc17 (diff) |
Bug 665: Allow re-association of GLContext/GLEventListener to a GLDrawable (Part 4)
Note:
- GLEventListenerState preservs the GLAutoDrawable state,
i.e. GLContext, all GLEventListener and the GLAnimatorControl association.
- GLEventListenerState may be utilized to move the state from a dying GLAutoDrawable,
to be moved to a new created GLAutoDrawable at a later time.
- GLEventListenerState will be made public soon.
+++
Exessive unit tests cover the new feature, tested manually on GNU/Linux/X11 and OSX(Java6/Java7).
+++
- GLAutoDrawable
- Change 'setContext(..)' to allow the destruction of the unbound old context:
'setContext(GLContext newCtx)' -> 'setContext(GLContext newCtx, boolean destroyPrevCtx)'
- Implementations: Properly implement 'setRealized(..)' incl. obeying threading constraints if exists.
Method is being utilized at least for GLEventListenerState.moveTo(..)
to unrealize and realize the drawable resources.
+++
Fix propagation of GLContext/GLDrawable association change (Bottom -> Top):
GLDrawableImpl.associateContext
GLContextImpl.associateDrawable
GLContextImpl.makeCurrent
GLContextImpl.destroy
GLContext.setGLDrawable
...
GLDrawableHelper.switchContext
GLAutoDrawble.setContext
associateDrawable(..)/associateContext(..) unifies and hence:
- GLContextImpl.contextRealized() (removed)
- GLDrawableImpl.contextRealized() (removed)
- GLDrawableImpl.associateContext(..) (merged)
- MacOSXCGLContext.drawableChangedNotify(..) (removed)
+++
- EGLUpstreamSurfaceHook.evalUpstreamSurface() validates the surface's device for reusage,
which is valid in case of GLEventListenerState.moveTo(..)
- MacOSXCGLContext.NSOpenGLImpl: pixelFormat replaces NSOpenGLLayerPfmt and has simplified lifecycle [create..destroy],
while native NSOpenGLLayer code only holds the reference until released.
26 files changed, 1113 insertions, 442 deletions
diff --git a/make/scripts/tests.sh b/make/scripts/tests.sh index 5d259d6be..8cc3fd84a 100755 --- a/make/scripts/tests.sh +++ b/make/scripts/tests.sh @@ -106,10 +106,11 @@ function jrun() { #D_ARGS="-Djogamp.debug=all" #D_ARGS="-Djogamp.debug=all -Dnativewindow.debug=all -Djogl.debug=all -Dnewt.debug=all" #D_ARGS="-Dnativewindow.debug=all -Djogl.debug=all -Dnewt.debug=all" - #D_ARGS="-Dnativewindow.debug.X11Util -Djogl.debug.EGLDisplayUtil -Djogl.debug.GLDrawable" + #D_ARGS="-Dnativewindow.debug.X11Util -Dnativewindow.debug.X11Util.TraceDisplayLifecycle -Djogl.debug.EGLDisplayUtil -Djogl.debug.GLDrawable" #D_ARGS="-Dnativewindow.debug.X11Util" #D_ARGS="-Djogl.debug.Animator" #D_ARGS="-Djogl.debug=all -Dnewt.debug=all" + #D_ARGS="-Djogl.debug.EGLDisplayUtil -Djogl.debug.GLDrawable" #D_ARGS="-Djogl.debug.GLDrawable -Djogl.debug.GLContext -Djogl.debug.GLCanvas" #D_ARGS="-Djogl.debug.GLDrawable" #D_ARGS="-Djogl.fbo.force.none" @@ -194,7 +195,6 @@ function jrun() { #D_ARGS="-Djogl.debug.GLContext -Dnewt.debug=all -Djogamp.debug.Lock -Djogamp.common.utils.locks.Lock.timeout=10000" #D_ARGS="-Djogl.debug.GLContext -Dnewt.debug=all" #D_ARGS="-Dnewt.debug=all" - #D_ARGS="-Dnewt.debug=all" #D_ARGS="-Djogl.debug.GLCanvas -Djogl.debug.GLJPanel" #X_ARGS="-Dsun.java2d.noddraw=True -Dsun.java2d.opengl=True -Dsun.java2d.xrender=false" #X_ARGS="-Dsun.java2d.noddraw=True -Dsun.java2d.opengl=false -Dsun.java2d.xrender=false" @@ -312,17 +312,9 @@ function testawtswt() { #testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestSharedContextListNEWT2 $* #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.TestGLContextDrawableSwitch01NEWT $* -#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLContextDrawableSwitch02NEWT $* #testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLPointsNEWT $* #testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLMesaBug651NEWT $* #testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLMesaBug658NEWT $* -#testnoawt com.jogamp.opengl.test.junit.jogl.acore.ect.TestExclusiveContext01VSyncAnimNEWT $* -#testawt com.jogamp.opengl.test.junit.jogl.acore.ect.TestExclusiveContext01VSyncAnimAWT $* -#testnoawt com.jogamp.opengl.test.junit.jogl.acore.ect.TestExclusiveContext02FPSAnimNEWT $* -#testawt com.jogamp.opengl.test.junit.jogl.acore.ect.TestExclusiveContext02FPSAnimAWT $* -#testnoawt com.jogamp.opengl.test.junit.jogl.acore.ect.TestExclusiveContext11VSyncAnimNEWT $* -#testnoawt com.jogamp.opengl.test.junit.jogl.acore.ect.TestExclusiveContext12FPSAnimNEWT $* #testawt com.jogamp.opengl.test.junit.jogl.acore.TestOffscreenLayer01GLCanvasAWT $* #testawt com.jogamp.opengl.test.junit.jogl.acore.TestOffscreenLayer02NewtCanvasAWT $* #testawt com.jogamp.opengl.test.junit.jogl.acore.TestAddRemove01GLCanvasSwingAWT $* @@ -339,15 +331,27 @@ function testawtswt() { #testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLAutoDrawableGLWindowOnOffscrnCapsNEWT $* #testawt com.jogamp.opengl.test.junit.jogl.acore.TestGLAutoDrawableNewtCanvasAWTOnOffscrnCapsAWT $* -#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLContextDrawableSwitch01NEWT $* -#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLContextDrawableSwitch11NEWT $* - #testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestFBOAutoDrawableFactoryNEWT $* #testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestFBOOffThreadSharedContextMix2DemosES2NEWT $* -testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestFBOOnThreadSharedContext1DemoES2NEWT $* +#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestFBOOnThreadSharedContext1DemoES2NEWT $* #testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestFBOMix2DemosES2NEWT $* #testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestFBOMRTNEWT01 $* +#testnoawt com.jogamp.opengl.test.junit.jogl.acore.ect.TestExclusiveContext01VSyncAnimNEWT $* +#testawt com.jogamp.opengl.test.junit.jogl.acore.ect.TestExclusiveContext01VSyncAnimAWT $* +#testnoawt com.jogamp.opengl.test.junit.jogl.acore.ect.TestExclusiveContext02FPSAnimNEWT $* +#testawt com.jogamp.opengl.test.junit.jogl.acore.ect.TestExclusiveContext02FPSAnimAWT $* +#testnoawt com.jogamp.opengl.test.junit.jogl.acore.ect.TestExclusiveContext11VSyncAnimNEWT $* +#testnoawt com.jogamp.opengl.test.junit.jogl.acore.ect.TestExclusiveContext12FPSAnimNEWT $* + +#testnoawt com.jogamp.opengl.test.junit.jogl.acore.glels.TestGLContextDrawableSwitch01NEWT $* +#testnoawt com.jogamp.opengl.test.junit.jogl.acore.glels.TestGLContextDrawableSwitch10NEWT $* +#testawt com.jogamp.opengl.test.junit.jogl.acore.glels.TestGLContextDrawableSwitch11NewtAWT $* +#testawt com.jogamp.opengl.test.junit.jogl.acore.glels.TestGLContextDrawableSwitch12AWT $* +testawt com.jogamp.opengl.test.junit.jogl.acore.glels.TestGLContextDrawableSwitch21Newt2AWT $* + +#testawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NewtCanvasAWT $* + #testawt com.jogamp.opengl.test.junit.jogl.acore.TestFBOAutoDrawableDeadlockAWT $* #testawt com.jogamp.opengl.test.junit.jogl.awt.TestBug461FBOSupersamplingSwingAWT @@ -560,9 +564,6 @@ testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestFBOOnThreadSharedContext1D # Child too low #testnoawt com.jogamp.opengl.test.junit.newt.parenting.TestParenting02NEWT $* -# NPE: -#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLContextDrawableSwitch11NEWT $* - # test rotation change #testnoawt com.jogamp.opengl.test.junit.newt.TestScreenMode02NEWT @@ -580,5 +581,15 @@ testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestFBOOnThreadSharedContext1D # ../../src/xcb_io.c:178: dequeue_pending_request: Assertion `!xcb_xlib_unknown_req_in_deq' failed. #testawt com.jogamp.opengl.test.junit.newt.parenting.TestParenting04AWT $* +# NEW +#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestFBOAutoDrawableFactoryNEWT $* +#testnoawt com.jogamp.opengl.test.junit.jogl.caps.TestBug605FlippedImageNEWT $* +#testnoawt com.jogamp.opengl.test.junit.jogl.caps.TestBug605FlippedImageAWT $* + +#testnoawt com.jogamp.opengl.test.junit.jogl.acore.glels.TestGLContextDrawableSwitch11NEWT $* + +#testawt com.jogamp.opengl.test.junit.newt.parenting.TestParenting04AWT $* + + $spath/count-edt-start.sh java-run.log diff --git a/src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java b/src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java index 0f0f03ac4..bec05a0bd 100644 --- a/src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java +++ b/src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java @@ -170,11 +170,6 @@ public class GLAutoDrawableDelegate extends GLAutoDrawableBase implements GLAuto } @Override - public final void setRealized(boolean realized) { - drawable.setRealized(realized); - } - - @Override public final void swapBuffers() throws GLException { defaultSwapBuffers(); } diff --git a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java index 80e1aa80d..169266152 100644 --- a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java +++ b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java @@ -705,16 +705,13 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { } @Override - public GLContext setContext(GLContext newCtx) { + public GLContext setContext(GLContext newCtx, boolean destroyPrevCtx) { final RecursiveLock _lock = lock; _lock.lock(); try { final GLContext oldCtx = context; - final boolean newCtxCurrent = GLDrawableHelper.switchContext(drawable, oldCtx, newCtx, additionalCtxCreationFlags); + GLDrawableHelper.switchContext(drawable, oldCtx, destroyPrevCtx, newCtx, additionalCtxCreationFlags); context=(GLContextImpl)newCtx; - if(newCtxCurrent) { - context.makeCurrent(); - } return oldCtx; } finally { _lock.unlock(); diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java b/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java index c03e4bfa4..83414ddb0 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java +++ b/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java @@ -151,7 +151,7 @@ public class GLDrawableUtil { for(int i = dest.getGLEventListenerCount() - 1; 0 <= i; i--) { dest.disposeGLEventListener(dest.getGLEventListener(i), false); } - dest.setContext( src.setContext( dest.getContext() ) ); + dest.setContext( src.setContext( dest.getContext(), false ), false ); src.invoke(true, GLEventListenerState.setViewport); dest.invoke(true, GLEventListenerState.setViewport); diff --git a/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java b/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java index 6abe4308b..bdbbd96a5 100644 --- a/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java +++ b/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java @@ -132,7 +132,8 @@ public interface GLAutoDrawable extends GLDrawable { /** * Associate the new context, <code>newtCtx</code>, to this auto-drawable. * <p> - * The current context will be dis-associated from this auto-drawable + * The current context will be destroyed if <code>destroyPrevCtx</code> is <code>true</code>, + * otherwise it will be dis-associated from this auto-drawable * via {@link GLContext#setGLDrawable(GLDrawable, boolean) setGLDrawable(null, true);} first. * </p> * <p> @@ -145,26 +146,16 @@ public interface GLAutoDrawable extends GLDrawable { * However the user shall take extra care that no other thread * attempts to make this context current. * </p> - * <p> - * In case you do not intend to use the old context anymore, i.e. - * not assigning it to another drawable, it shall be - * destroyed, i.e.: - * <pre> - GLContext oldCtx = glad.setContext(newCtx); - if(null != oldCtx) { - oldCtx.destroy(); - } - * </pre> - * </p> * * @param newCtx the new context, maybe <code>null</code> for dis-association. + * @param destroyPrevCtx if <code>true</code>, destroy the previous context if exists * @return the previous GLContext, maybe <code>null</code> * * @see GLContext#setGLDrawable(GLDrawable, boolean) * @see GLContext#setGLReadDrawable(GLDrawable) - * @see jogamp.opengl.GLDrawableHelper#switchContext(GLDrawable, GLContext, GLContext, int) + * @see jogamp.opengl.GLDrawableHelper#switchContext(GLDrawable, GLContext, boolean, GLContext, int) */ - public GLContext setContext(GLContext newCtx); + public GLContext setContext(GLContext newCtx, boolean destroyPrevCtx); /** * Adds the given {@link GLEventListener listener} to the end of this drawable queue. @@ -500,7 +491,7 @@ public interface GLAutoDrawable extends GLDrawable { * <p> * This GLAutoDrawable implementation holds it's own GLContext reference, * thus created a GLContext using this methods won't replace it implicitly. - * To replace or set this GLAutoDrawable's GLContext you need to call {@link #setContext(GLContext)}. + * To replace or set this GLAutoDrawable's GLContext you need to call {@link #setContext(GLContext, boolean)}. * </p> * <p> * The GLAutoDrawable implementation shall also set the diff --git a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java index ebc25e2ad..63c18db5d 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java @@ -429,8 +429,29 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing } } + private final void setRealizedImpl(boolean realized) { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + final GLDrawable _drawable = drawable; + if( null == _drawable || realized && ( 0 >= _drawable.getWidth() || 0 >= _drawable.getHeight() ) ) { + return; + } + _drawable.setRealized(realized); + if( realized && _drawable.isRealized() ) { + sendReshape=true; // ensure a reshape is being send .. + } + } finally { + _lock.unlock(); + } + } + private final Runnable realizeOnEDTAction = new Runnable() { public void run() { setRealizedImpl(true); } }; + private final Runnable unrealizeOnEDTAction = new Runnable() { public void run() { setRealizedImpl(false); } }; + @Override - public void setRealized(boolean realized) { + public final void setRealized(boolean realized) { + // Make sure drawable realization happens on AWT-EDT and only there. Consider the AWTTree lock! + AWTEDTExecutor.singleton.invoke(getTreeLock(), false /* allowOnNonEDT */, true /* wait */, realized ? realizeOnEDTAction : unrealizeOnEDTAction); } @Override @@ -595,46 +616,28 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing } private boolean validateGLDrawable() { - if( Beans.isDesignTime() || !isDisplayable() ) { - return false; // early out! - } - final GLDrawable _drawable = drawable; - if ( null != _drawable ) { - if( _drawable.isRealized() ) { - return true; - } - if( 0 >= _drawable.getWidth() || 0 >= _drawable.getHeight() ) { - return false; // early out! - } - // Make sure drawable realization happens on AWT-EDT and only there. Consider the AWTTree lock! - final boolean res0 = AWTEDTExecutor.singleton.invoke(getTreeLock(), false /* allowOnNonEDT */, true /* wait */, setRealizedOnEDTAction); - final boolean res1 = res0 && _drawable.isRealized(); - if(DEBUG) { - System.err.println(getThreadName()+": Realized Drawable: invoked "+res0+", probedIsRealized "+res1+", "+_drawable.toString()); - Thread.dumpStack(); - } - return res1; - } - return false; - } - private Runnable setRealizedOnEDTAction = new Runnable() { - public void run() { - final RecursiveLock _lock = lock; - _lock.lock(); - try { - final GLDrawable _drawable = drawable; - if( null == _drawable || 0 >= _drawable.getWidth() || 0 >= _drawable.getHeight() ) { - return; - } - _drawable.setRealized(true); - if( _drawable.isRealized() ) { - sendReshape=true; // ensure a reshape is being send .. - } - } finally { - _lock.unlock(); + if( Beans.isDesignTime() || !isDisplayable() ) { + return false; // early out! + } + final GLDrawable _drawable = drawable; + if ( null != _drawable ) { + if( _drawable.isRealized() ) { + return true; } - } }; - + if( 0 >= _drawable.getWidth() || 0 >= _drawable.getHeight() ) { + return false; // early out! + } + setRealized(true); + final boolean res = _drawable.isRealized(); + if(DEBUG) { + System.err.println(getThreadName()+": Realized Drawable: isRealized "+res+", "+_drawable.toString()); + Thread.dumpStack(); + } + return res; + } + return false; + } + /** <p>Overridden to track when this component is removed from a container. Subclasses which override this method must call super.removeNotify() in their removeNotify() method in order to @@ -791,16 +794,13 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing } @Override - public GLContext setContext(GLContext newCtx) { + public GLContext setContext(GLContext newCtx, boolean destroyPrevCtx) { final RecursiveLock _lock = lock; _lock.lock(); try { final GLContext oldCtx = context; - final boolean newCtxCurrent = GLDrawableHelper.switchContext(drawable, oldCtx, newCtx, additionalCtxCreationFlags); + GLDrawableHelper.switchContext(drawable, oldCtx, destroyPrevCtx, newCtx, additionalCtxCreationFlags); context=(GLContextImpl)newCtx; - if(newCtxCurrent) { - context.makeCurrent(); - } return oldCtx; } finally { _lock.unlock(); diff --git a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java index 8d9493cbf..d62967d7f 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java @@ -519,16 +519,13 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } @Override - public GLContext setContext(GLContext newCtx) { + public GLContext setContext(GLContext newCtx, boolean destroyPrevCtx) { if (backend == null) { return null; } final GLContext oldCtx = backend.getContext(); - final boolean newCtxCurrent = GLDrawableHelper.switchContext(backend.getDrawable(), oldCtx, newCtx, additionalCtxCreationFlags); + GLDrawableHelper.switchContext(backend.getDrawable(), oldCtx, destroyPrevCtx, newCtx, additionalCtxCreationFlags); backend.setContext(newCtx); - if(newCtxCurrent) { - newCtx.makeCurrent(); - } return oldCtx; } diff --git a/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java b/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java index c20197e72..ad67f8281 100644 --- a/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java +++ b/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java @@ -326,16 +326,13 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, FPSCounter { } @Override - public final GLContext setContext(GLContext newCtx) { + public final GLContext setContext(GLContext newCtx, boolean destroyPrevCtx) { final RecursiveLock lock = getLock(); lock.lock(); try { final GLContext oldCtx = context; - final boolean newCtxCurrent = GLDrawableHelper.switchContext(drawable, oldCtx, newCtx, additionalCtxCreationFlags); + GLDrawableHelper.switchContext(drawable, oldCtx, destroyPrevCtx, newCtx, additionalCtxCreationFlags); context=(GLContextImpl)newCtx; - if(newCtxCurrent) { // implies null != newCtx - context.makeCurrent(); - } return oldCtx; } finally { lock.unlock(); @@ -531,6 +528,24 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, FPSCounter { } @Override + public final void setRealized(boolean realized) { + final RecursiveLock _lock = getLock(); + _lock.lock(); + try { + final GLDrawable _drawable = drawable; + if( null == _drawable || realized && ( 0 >= _drawable.getWidth() || 0 >= _drawable.getHeight() ) ) { + return; + } + _drawable.setRealized(realized); + if( realized && _drawable.isRealized() ) { + sendReshape=true; // ensure a reshape is being send .. + } + } finally { + _lock.unlock(); + } + } + + @Override public final boolean isRealized() { final GLDrawable _drawable = drawable; return null != _drawable ? _drawable.isRealized() : false; diff --git a/src/jogl/classes/jogamp/opengl/GLContextImpl.java b/src/jogl/classes/jogamp/opengl/GLContextImpl.java index 42364dbfd..4121e8f77 100644 --- a/src/jogl/classes/jogamp/opengl/GLContextImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLContextImpl.java @@ -133,9 +133,6 @@ public abstract class GLContextImpl extends GLContext { GLContextShareSet.synchronizeBufferObjectSharing(shareWith, this); this.drawable = drawable; - if(null != drawable) { - drawable.associateContext(this, true); - } this.drawableRead = drawable; this.glDebugHandler = new GLDebugMessageHandler(this); @@ -207,25 +204,36 @@ public abstract class GLContextImpl extends GLContext { return drawable; // no change. } final Thread currentThread = Thread.currentThread(); + if( lock.isLockedByOtherThread() ) { + throw new GLException("GLContext current by other thread "+lock.getOwner().getName()+", operation not allowed on this thread "+currentThread.getName()); + } final boolean lockHeld = lock.isOwner(currentThread); + if( lockHeld && lock.getHoldCount() > 1 ) { + // would need to makeCurrent * holdCount + throw new GLException("GLContext is recursively locked - unsupported for setGLDrawable(..)"); + } + final GLDrawableImpl old = drawable; + if( isCreated() && null != old && old.isRealized() ) { + if(!lockHeld) { + makeCurrent(); + } + associateDrawable(false); + if(!lockHeld) { + release(); + } + } if(lockHeld) { release(); - } else if(lock.isLockedByOtherThread()) { // still could glitch .. - throw new GLException("GLContext current by other thread "+lock.getOwner().getName()+", operation not allowed on this thread "+currentThread.getName()); - } + } if( !setWriteOnly || drawableRead == drawable ) { // if !setWriteOnly || !explicitReadDrawable drawableRead = (GLDrawableImpl) readWrite; } - final GLDrawableImpl old = drawable; - if( null != old ) { - old.associateContext(this, false); - } drawableRetargeted |= null != drawable && readWrite != drawable; drawable = (GLDrawableImpl) readWrite ; - if( null != drawable ) { - drawable.associateContext(this, true); - if( lockHeld ) { - makeCurrent(); + if( isCreated() && null != drawable && drawable.isRealized() ) { + makeCurrent(true); // implicit: associateDrawable(true) + if( !lockHeld ) { + release(); } } return old; @@ -363,8 +371,7 @@ public abstract class GLContextImpl extends GLContext { makeCurrent(); } try { - contextRealized(false); - drawable.associateContext(this, false); + associateDrawable(false); } catch (Throwable t) { drawableContextRealizedException = t; } @@ -467,7 +474,11 @@ public abstract class GLContextImpl extends GLContext { * @see #destroyContextARBImpl */ @Override - public int makeCurrent() throws GLException { + public final int makeCurrent() throws GLException { + return makeCurrent(false); + } + + protected final int makeCurrent(boolean forceDrawableAssociation) throws GLException { if( TRACE_SWITCH ) { System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.0]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+" - "+lock); } @@ -561,7 +572,11 @@ public abstract class GLContextImpl extends GLContext { gl = gl.getContext().setGL( GLPipelineFactory.create("javax.media.opengl.Trace", null, gl, new Object[] { System.err } ) ); } - contextRealized(true); + forceDrawableAssociation = true; + } + + if( forceDrawableAssociation ) { + associateDrawable(true); } contextMadeCurrent(true); @@ -662,14 +677,14 @@ public abstract class GLContextImpl extends GLContext { protected abstract void makeCurrentImpl() throws GLException; /** - * @see GLDrawableImpl#contextRealized(GLContext, boolean) + * Calls {@link GLDrawableImpl#associateContext(GLContext, boolean)} */ - protected void contextRealized(boolean realized) { - drawable.contextRealized(this, realized); + protected void associateDrawable(boolean bound) { + drawable.associateContext(this, bound); } /** - * @see GLDrawableImpl#contextMadeCurrent(GLContext, boolean) + * Calls {@link GLDrawableImpl#contextMadeCurrent(GLContext, boolean)} */ protected void contextMadeCurrent(boolean current) { drawable.contextMadeCurrent(this, current); diff --git a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java index 3eedf918e..1caa942ba 100644 --- a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java +++ b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java @@ -59,7 +59,7 @@ import javax.media.opengl.GLFBODrawable; import javax.media.opengl.GLRunnable; /** Encapsulates the implementation of most of the GLAutoDrawable's - methods to be able to share it between GLCanvas and GLJPanel. */ + methods to be able to share it between GLAutoDrawable implementations like GLAutoDrawableBase, 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); @@ -113,6 +113,9 @@ public class GLDrawableHelper { return sb.toString(); } + /** Limit release calls of {@link #forceNativeRelease(GLContext)} to {@value}. */ + private static final int MAX_RELEASE_ITER = 512; + /** * Since GLContext's {@link GLContext#makeCurrent()} and {@link GLContext#release()} * is recursive, a call to {@link GLContext#release()} may not natively release the context. @@ -122,18 +125,25 @@ public class GLDrawableHelper { * @param ctx */ public static final void forceNativeRelease(GLContext ctx) { + int releaseCount = 0; do { ctx.release(); + releaseCount++; if (DEBUG) { - System.err.println("GLDrawableHelper.forceNativeRelease() -- currentThread "+Thread.currentThread()+" -> "+GLContext.getCurrent()); + System.err.println("GLDrawableHelper.forceNativeRelease() #"+releaseCount+" -- currentThread "+Thread.currentThread()+" -> "+GLContext.getCurrent()); } - } while( ctx == GLContext.getCurrent() ); + } while( MAX_RELEASE_ITER > releaseCount && ctx.isCurrent() ); + + if( ctx.isCurrent() ) { + throw new GLException("Context still current after "+MAX_RELEASE_ITER+" releases: "+ctx); + } } /** * Switch {@link GLContext} / {@link GLDrawable} association. * <p> - * Dis-associate <code>oldCtx</code> from <code>drawable</code> + * The <code>oldCtx</code> will be destroyed if <code>destroyPrevCtx</code> is <code>true</code>, + * otherwise dis-associate <code>oldCtx</code> from <code>drawable</code> * via {@link GLContext#setGLDrawable(GLDrawable, boolean) oldCtx.setGLDrawable(null, true);}. * </p> * <p> @@ -149,31 +159,25 @@ public class GLDrawableHelper { * * @param drawable the drawable which context is changed * @param oldCtx the old context, maybe <code>null</code>. + * @param destroyOldCtx if <code>true</code>, destroy the <code>oldCtx</code> * @param newCtx the new context, maybe <code>null</code> for dis-association. * @param newCtxCreationFlags additional creation flags if newCtx is not null and not been created yet, see {@link GLContext#setContextCreationFlags(int)} - * @return true if the new context was current, otherwise false * - * @see GLAutoDrawable#setContext(GLContext) + * @see GLAutoDrawable#setContext(GLContext, boolean) */ - public static final boolean switchContext(GLDrawable drawable, GLContext oldCtx, GLContext newCtx, int newCtxCreationFlags) { + public static final void switchContext(GLDrawable drawable, GLContext oldCtx, boolean destroyOldCtx, GLContext newCtx, int newCtxCreationFlags) { if( null != oldCtx ) { - if( oldCtx.isCurrent() ) { - oldCtx.release(); + if( destroyOldCtx ) { + oldCtx.destroy(); + } else { + oldCtx.setGLDrawable(null, true); // dis-associate old pair } - oldCtx.setGLDrawable(null, true); // dis-associate old pair } - final boolean newCtxCurrent; + if(null!=newCtx) { - newCtxCurrent = newCtx.isCurrent(); - if(newCtxCurrent) { - newCtx.release(); - } newCtx.setContextCreationFlags(newCtxCreationFlags); - newCtx.setGLDrawable(drawable, true); // re-associate new pair - } else { - newCtxCurrent = false; + newCtx.setGLDrawable(drawable, true); // re-associate new pair } - return newCtxCurrent; } /** @@ -208,7 +212,6 @@ public class GLDrawableHelper { context.makeCurrent(); } context.getGL().glFinish(); - context.release(); context.setGLDrawable(null, true); // dis-associate } @@ -837,7 +840,7 @@ public class GLDrawableHelper { if( null != exclusiveContextThread ) { throw new GLException("Release current exclusive Context Thread "+exclusiveContextThread+" first"); } - if( null != context && GLContext.getCurrent() == context ) { + if( null != context && context.isCurrent() ) { try { forceNativeRelease(context); } catch (Throwable ex) { diff --git a/src/jogl/classes/jogamp/opengl/GLDrawableImpl.java b/src/jogl/classes/jogamp/opengl/GLDrawableImpl.java index c0c28a5f2..d0c1461a9 100644 --- a/src/jogl/classes/jogamp/opengl/GLDrawableImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLDrawableImpl.java @@ -164,13 +164,14 @@ public abstract class GLDrawableImpl implements GLDrawable { @Override public final void setRealized(boolean realizedArg) { if ( realized != realizedArg ) { // volatile: OK (locked below) + final boolean isProxySurface = surface instanceof ProxySurface; if(DEBUG) { - System.err.println(getThreadName() + ": setRealized: "+getClass().getSimpleName()+" "+realized+" -> "+realizedArg); + System.err.println(getThreadName() + ": setRealized: drawable "+getClass().getSimpleName()+", surface "+surface.getClass().getSimpleName()+", isProxySurface "+isProxySurface+": "+realized+" -> "+realizedArg); Thread.dumpStack(); } AbstractGraphicsDevice aDevice = surface.getGraphicsConfiguration().getScreen().getDevice(); if(realizedArg) { - if(surface instanceof ProxySurface) { + if(isProxySurface) { ((ProxySurface)surface).createNotify(); } if(NativeSurface.LOCK_SURFACE_NOT_READY >= lockSurface()) { @@ -195,7 +196,7 @@ public abstract class GLDrawableImpl implements GLDrawable { unlockSurface(); } else { aDevice.unlock(); - if(surface instanceof ProxySurface) { + if(isProxySurface) { ((ProxySurface)surface).destroyNotify(); } } @@ -210,18 +211,26 @@ public abstract class GLDrawableImpl implements GLDrawable { */ protected abstract void setRealizedImpl(); - /** - * Callback for special implementations, allowing GLContext to trigger GL related lifecycle: <code>construct</code>, <code>destroy</code>. + /** + * Callback for special implementations, allowing + * <ul> + * <li>to associate bound context to this drawable (bound == true) + * or to remove such association (bound == false).</li> + * <li>to trigger GLContext/GLDrawable related lifecycle: <code>construct</code>, <code>destroy</code>.</li> + * </ul> * <p> - * If <code>realized</code> is <code>true</code>, the context has just been created and made current. + * If <code>bound</code> is <code>true</code>, the context is current and being newly associated w/ this drawable. * </p> * <p> - * If <code>realized</code> is <code>false</code>, the context is still current and will be released and destroyed after this method returns. + * If <code>bound</code> is <code>false</code>, the context is still current and will be unbound (released and destroyed, or simply disassociated). * </p> * <p> - * @see #contextMadeCurrent(GLContext, boolean) + * Being called by {@link GLContextImpl#associateDrawable(boolean)}. + * </p> + * @param ctx the just bounded or unbounded context + * @param bound if <code>true</code> create an association, otherwise remove it */ - protected void contextRealized(GLContext glc, boolean realized) {} + protected void associateContext(GLContext ctx, boolean bound) { } /** * Callback for special implementations, allowing GLContext to trigger GL related lifecycle: <code>makeCurrent</code>, <code>release</code>. @@ -232,21 +241,12 @@ public abstract class GLDrawableImpl implements GLDrawable { * If <code>current</code> is <code>false</code>, the context is still current and will be release after this method returns. * </p> * <p> - * Note: Will also be called after {@link #contextRealized(GLContext, boolean) contextRealized(ctx, true)} - * but not at context destruction, i.e. {@link #contextRealized(GLContext, boolean) contextRealized(ctx, false)}. + * Being called by {@link GLContextImpl#contextMadeCurrent(boolean)}. * </p> - * @see #contextRealized(GLContext, boolean) + * @see #associateContext(GLContext, boolean) */ protected void contextMadeCurrent(GLContext glc, boolean current) { } - /** - * Callback for special implementations, allowing to associate bound context to this drawable (bound == true) - * or to remove such association (bound == false). - * @param ctx the just bounded or unbounded context - * @param bound if <code>true</code> create an association, otherwise remove it - */ - protected void associateContext(GLContext ctx, boolean bound) { } - /** Callback for special implementations, allowing GLContext to fetch a custom default render framebuffer. Defaults to zero.*/ protected int getDefaultDrawFramebuffer() { return 0; } /** Callback for special implementations, allowing GLContext to fetch a custom default read framebuffer. Defaults to zero. */ diff --git a/src/jogl/classes/jogamp/opengl/GLEventListenerState.java b/src/jogl/classes/jogamp/opengl/GLEventListenerState.java index 7a2569850..2385460fe 100644 --- a/src/jogl/classes/jogamp/opengl/GLEventListenerState.java +++ b/src/jogl/classes/jogamp/opengl/GLEventListenerState.java @@ -45,7 +45,6 @@ import javax.media.opengl.GLEventListener; import javax.media.opengl.GLException; import javax.media.opengl.GLRunnable; -import com.jogamp.common.util.locks.RecursiveLock; import com.jogamp.nativewindow.MutableGraphicsConfiguration; /** @@ -71,11 +70,12 @@ import com.jogamp.nativewindow.MutableGraphicsConfiguration; * <p> */ public class GLEventListenerState { - private static final boolean DEBUG = true; + private static final boolean DEBUG = GLDrawableImpl.DEBUG; - private GLEventListenerState(AbstractGraphicsScreen upstreamScreen, AbstractGraphicsScreen screen, GLCapabilitiesImmutable caps, + private GLEventListenerState(AbstractGraphicsScreen upstreamScreen, boolean proxyOwnsUpstreamDevice, AbstractGraphicsScreen screen, GLCapabilitiesImmutable caps, GLContext context, int count, GLAnimatorControl anim) { this.upstreamScreen = upstreamScreen; + this.proxyOwnsUpstreamDevice = proxyOwnsUpstreamDevice; this.screen = screen; this.caps = caps; this.context = context; @@ -97,6 +97,7 @@ public class GLEventListenerState { public final int listenerCount() { return listeners.length; } public final AbstractGraphicsScreen upstreamScreen; + public final boolean proxyOwnsUpstreamDevice; public final AbstractGraphicsScreen screen; public final GLCapabilitiesImmutable caps; public final GLContext context; @@ -161,25 +162,30 @@ public class GLEventListenerState { } aScreen1.getDevice().clearHandleOwner(); // don't close device handle - final AbstractGraphicsScreen aUpScreen2; + final AbstractGraphicsScreen aUpScreen2; + final boolean proxyOwnsUpstreamDevice; { AbstractGraphicsScreen _aUpScreen2=null; if(aSurface instanceof ProxySurface) { final ProxySurface aProxy = (ProxySurface)aSurface; + proxyOwnsUpstreamDevice = aProxy.containsUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ); final NativeSurface aUpSurface = aProxy.getUpstreamSurface(); - if(null != aUpSurface) { + if(DEBUG && null != aUpSurface) { System.err.println("X00 UpstreamSurface: "+aUpSurface.getClass().getName()+", "+aUpSurface); } - aProxy.clearUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ); // don't close device handle if(null != aUpSurface) { final AbstractGraphicsScreen aUpScreen1 = aUpSurface.getGraphicsConfiguration().getScreen(); _aUpScreen2 = cloneScreen(aUpScreen1); if(null != aUpScreen1) { aUpScreen1.getDevice().clearHandleOwner(); // don't close device handle } - System.err.println("X0X NativeSurface: "+aSurface.getClass().getName()+", "+aSurface); - System.err.println("X0X UpstreamSurface: "+aUpSurface.getClass().getName()+", "+aUpSurface); + if(DEBUG) { + System.err.println("X0X NativeSurface: "+aSurface.getClass().getName()+", "+aSurface); + System.err.println("X0X UpstreamSurface: "+aUpSurface.getClass().getName()+", "+aUpSurface); + } } + } else { + proxyOwnsUpstreamDevice = false; } aUpScreen2=_aUpScreen2; } @@ -189,7 +195,7 @@ public class GLEventListenerState { aAnim.remove(a); // also handles ECT } - final GLEventListenerState glls = new GLEventListenerState(aUpScreen2, aScreen2, caps, a.getContext(), aSz, aAnim); + final GLEventListenerState glls = new GLEventListenerState(aUpScreen2, proxyOwnsUpstreamDevice, aScreen2, caps, a.getContext(), aSz, aAnim); // // remove and cache all GLEventListener and their init-state @@ -205,7 +211,7 @@ public class GLEventListenerState { // a.invoke(true, glFinish); - a.setContext( null ); + a.setContext( null, false ); return glls; } @@ -241,13 +247,7 @@ public class GLEventListenerState { throw new GLException("Incompatible Capabilities - Prev-Holder: "+caps+", New-Holder "+caps); } // Destroy and remove currently associated GLContext, if any (will be replaced) - { - final GLContext ctx = a.getContext(); - if( null != ctx) { - ctx.destroy(); - } - a.setContext( null ); - } + a.setContext( null, true ); final boolean aRealized = a.isRealized(); if( aRealized ) { a.setRealized(false); @@ -260,8 +260,11 @@ public class GLEventListenerState { final AbstractGraphicsScreen aScreen1 = aCfg.getScreen(); aCfg.setScreen( screen ); aScreen1.getDevice().close(); - System.err.println("XXX NativeSurface: "+aSurface.getClass().getName()+", "+aSurface); + if( DEBUG ) { + System.err.println("XXX NativeSurface: "+aSurface.getClass().getName()+", "+aSurface); + } } + // If using a ProxySurface w/ an upstream surface, set new Screen and close previous one on it { boolean upstreamSet = false; @@ -270,13 +273,19 @@ public class GLEventListenerState { final NativeSurface aUpSurface = aProxy.getUpstreamSurface(); if(null != aUpSurface) { final MutableGraphicsConfiguration aUpCfg = (MutableGraphicsConfiguration) aUpSurface.getGraphicsConfiguration(); - final AbstractGraphicsScreen aUpScreen1 = aUpCfg.getScreen(); if( null != upstreamScreen ) { - System.err.println("XX0 UpstreamSurface: "+aUpSurface.getClass().getName()+", "+aUpSurface); + if( DEBUG ) { + System.err.println("XX0 UpstreamSurface: "+aUpSurface.getClass().getName()+", "+aUpSurface+", "+aProxy.getUpstreamOptionBits(null).toString()); + } + aUpCfg.getScreen().getDevice().close(); aUpCfg.setScreen( upstreamScreen ); - aUpScreen1.getDevice().close(); + if( proxyOwnsUpstreamDevice ) { + aProxy.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ); + } upstreamSet = true; - System.err.println("XXX UpstreamSurface: "+aUpSurface.getClass().getName()+", "+aUpSurface); + if( DEBUG ) { + System.err.println("XXX UpstreamSurface: "+aUpSurface.getClass().getName()+", "+aUpSurface+", "+aProxy.getUpstreamOptionBits(null).toString()); + } } else { throw new GLException("Incompatible Surface config - Has Upstream-Surface: Prev-Holder = false, New-Holder = true"); } @@ -292,7 +301,7 @@ public class GLEventListenerState { } final boolean surfaceLocked = false; // NativeSurface.LOCK_SURFACE_NOT_READY < aSurface.lockSurface(); try { - a.setContext( context ); + a.setContext( context, false ); } finally { if( surfaceLocked ) { aSurface.unlockSurface(); diff --git a/src/jogl/classes/jogamp/opengl/GLFBODrawableImpl.java b/src/jogl/classes/jogamp/opengl/GLFBODrawableImpl.java index 86cfa4f4c..51ec7dda6 100644 --- a/src/jogl/classes/jogamp/opengl/GLFBODrawableImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLFBODrawableImpl.java @@ -38,6 +38,7 @@ import com.jogamp.opengl.JoglVersion; */ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { protected static final boolean DEBUG = GLDrawableImpl.DEBUG || Debug.debug("FBObject"); + protected static final boolean DEBUG_SWAP = Debug.isPropertyDefined("jogl.debug.FBObject.Swap", true); private final GLDrawableImpl parent; private GLCapabilitiesImmutable origParentChosenCaps; @@ -92,6 +93,9 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { } private final void initialize(boolean realize, GL gl) { + if( initialized == realize ) { + throw new InternalError("Already set to initialize := "+realize+": "+this); + } if(realize) { final GLCapabilities chosenFBOCaps = (GLCapabilities) getChosenGLCapabilities(); // cloned at setRealized(true) @@ -323,8 +327,8 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { } @Override - protected final void contextRealized(GLContext glc, boolean realized) { - initialize(realized, glc.getGL()); + protected void associateContext(GLContext glc, boolean bound) { + initialize(bound, glc.getGL()); } @Override @@ -338,7 +342,7 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { swapFBOImpl(glc); swapFBOImplPost(glc); fboBound=false; - if(DEBUG) { + if(DEBUG_SWAP) { System.err.println("Post FBO swap(@release): done"); } } @@ -354,7 +358,7 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { swapFBOImpl(ctx); doPostSwap = true; fboBound=false; - if(DEBUG) { + if(DEBUG_SWAP) { System.err.println("Post FBO swap(@swap): done"); } } @@ -406,7 +410,7 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { gl.glBindFramebuffer(GL2GL3.GL_READ_FRAMEBUFFER, fbos[fboIFront].getReadFramebuffer()); } */ - if(DEBUG) { + if(DEBUG_SWAP) { System.err.println("Post FBO swap(X): fboI back "+fboIBack+", front "+fboIFront+", num "+fbos.length); } } diff --git a/src/jogl/classes/jogamp/opengl/GLPbufferImpl.java b/src/jogl/classes/jogamp/opengl/GLPbufferImpl.java index b438131bc..ddc6d5917 100644 --- a/src/jogl/classes/jogamp/opengl/GLPbufferImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLPbufferImpl.java @@ -86,9 +86,10 @@ public class GLPbufferImpl extends GLAutoDrawableBase implements GLPbuffer { // @Override - public final void setRealized(boolean realized) { + public final void swapBuffers() throws GLException { + defaultSwapBuffers(); } - + // // GLAutoDrawable completion // @@ -125,11 +126,6 @@ public class GLPbufferImpl extends GLAutoDrawableBase implements GLPbuffer { } } - @Override - public final void swapBuffers() throws GLException { - defaultSwapBuffers(); - } - //---------------------------------------------------------------------- // Internals only below this point // diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLUpstreamSurfaceHook.java b/src/jogl/classes/jogamp/opengl/egl/EGLUpstreamSurfaceHook.java index 342c4c417..77e9dc173 100644 --- a/src/jogl/classes/jogamp/opengl/egl/EGLUpstreamSurfaceHook.java +++ b/src/jogl/classes/jogamp/opengl/egl/EGLUpstreamSurfaceHook.java @@ -47,7 +47,7 @@ public class EGLUpstreamSurfaceHook implements UpstreamSurfaceHook.MutableSize { public final void create(ProxySurface surface) { final String dbgPrefix; if(DEBUG) { - dbgPrefix = getThreadName() + ": EGLUpstreamSurfaceHook.create("+surface.getClass().getSimpleName()+"): "; + dbgPrefix = getThreadName() + ": EGLUpstreamSurfaceHook.create( up "+upstreamSurface.getClass().getSimpleName()+" -> this "+surface.getClass().getSimpleName()+" ): "; System.err.println(dbgPrefix+this); } else { dbgPrefix = null; @@ -76,24 +76,51 @@ public class EGLUpstreamSurfaceHook implements UpstreamSurfaceHook.MutableSize { boolean isEGLSurfaceValid = true; // assume yes - final AbstractGraphicsConfiguration aConfig = upstreamSurface.getGraphicsConfiguration(); - final AbstractGraphicsDevice aDevice = aConfig.getScreen().getDevice(); - final EGLGraphicsDevice eglDevice; - if( aDevice instanceof EGLGraphicsDevice ) { - eglDevice = (EGLGraphicsDevice) aDevice; + final AbstractGraphicsConfiguration aConfig; + { + final AbstractGraphicsConfiguration surfaceConfig = surface.getGraphicsConfiguration(); + final AbstractGraphicsDevice surfaceDevice = null != surfaceConfig ? surfaceConfig.getScreen().getDevice() : null; + if(DEBUG) { + System.err.println(dbgPrefix+"SurfaceDevice: "+surfaceDevice.getClass().getSimpleName()+", hash 0x"+Integer.toHexString(surfaceDevice.hashCode())+", "+surfaceDevice); + System.err.println(dbgPrefix+"SurfaceConfig: "+surfaceConfig.getClass().getSimpleName()+", hash 0x"+Integer.toHexString(surfaceConfig.hashCode())+", "+surfaceConfig); + } + + final AbstractGraphicsConfiguration upstreamConfig = upstreamSurface.getGraphicsConfiguration(); + final AbstractGraphicsDevice upstreamDevice = upstreamConfig.getScreen().getDevice(); if(DEBUG) { - System.err.println(dbgPrefix+"Reusing eglDevice: "+eglDevice); + System.err.println(dbgPrefix+"UpstreamDevice: "+upstreamDevice.getClass().getSimpleName()+", hash 0x"+Integer.toHexString(upstreamDevice.hashCode())+", "+upstreamDevice); + System.err.println(dbgPrefix+"UpstreamConfig: "+upstreamConfig.getClass().getSimpleName()+", hash 0x"+Integer.toHexString(upstreamConfig.hashCode())+", "+upstreamConfig); } - if(EGL.EGL_NO_DISPLAY == eglDevice.getHandle()) { - eglDevice.open(); + + if( surfaceDevice instanceof EGLGraphicsDevice ) { + eglDevice = (EGLGraphicsDevice) surfaceDevice; + aConfig = surfaceConfig; + if(DEBUG) { + System.err.println(dbgPrefix+"Reusing this eglDevice: "+eglDevice+", using this config "+aConfig.getClass().getSimpleName()+" "+aConfig); + } + if(EGL.EGL_NO_DISPLAY == eglDevice.getHandle()) { + eglDevice.open(); + isEGLSurfaceValid = false; + surface.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ); + } + } else if( upstreamDevice instanceof EGLGraphicsDevice ) { + eglDevice = (EGLGraphicsDevice) upstreamDevice; + aConfig = upstreamConfig; + if(DEBUG) { + System.err.println(dbgPrefix+"Reusing upstream eglDevice: "+eglDevice+", using upstream config "+aConfig.getClass().getSimpleName()+" "+aConfig); + } + if(EGL.EGL_NO_DISPLAY == eglDevice.getHandle()) { + eglDevice.open(); + isEGLSurfaceValid = false; + surface.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ); + } + } else { + eglDevice = EGLDisplayUtil.eglCreateEGLGraphicsDevice(upstreamSurface); + aConfig = upstreamConfig; isEGLSurfaceValid = false; surface.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ); } - } else { - eglDevice = EGLDisplayUtil.eglCreateEGLGraphicsDevice(upstreamSurface); - isEGLSurfaceValid = false; - surface.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ); } final GLCapabilitiesImmutable capsRequested = (GLCapabilitiesImmutable) aConfig.getRequestedCapabilities(); diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java index 3825f855c..a03850043 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java @@ -85,10 +85,9 @@ public abstract class MacOSXCGLContext extends GLContextImpl // NSOpenGL-based or CGL-based) protected interface GLBackendImpl { boolean isNSContext(); - void drawableChangedNotify(); long create(long share, int ctp, int major, int minor); boolean destroy(long ctx); - boolean contextRealized(boolean realized); + void associateDrawable(boolean bound); boolean copyImpl(long src, int mask); boolean makeCurrent(long ctx); boolean release(long ctx); @@ -328,20 +327,15 @@ public abstract class MacOSXCGLContext extends GLContextImpl } @Override - protected void contextRealized(boolean realized) { + protected void associateDrawable(boolean bound) { // context stuff depends on drawable stuff - if(realized) { - super.contextRealized(true); // 1) init drawable stuff - impl.contextRealized(true); // 2) init context stuff + System.err.println("MaxOSXCGLContext.associateDrawable: "+bound); + if(bound) { + super.associateDrawable(true); // 1) init drawable stuff + impl.associateDrawable(true); // 2) init context stuff } else { - impl.contextRealized(false); // 1) free context stuff - super.contextRealized(false); // 2) free drawable stuff - } - } - - /* pp */ void drawableChangedNotify() { - if( 0 != contextHandle) { - impl.drawableChangedNotify(); + impl.associateDrawable(false); // 1) free context stuff + super.associateDrawable(false); // 2) free drawable stuff } } @@ -467,9 +461,8 @@ public abstract class MacOSXCGLContext extends GLContextImpl // NSOpenGLContext-based implementation class NSOpenGLImpl implements GLBackendImpl { - private OffscreenLayerSurface backingLayerHost = null; - private long nsOpenGLLayer = 0; - private long nsOpenGLLayerPFmt = 0; // lifecycle: [create - contextRealized] + private long pixelFormat = 0; // lifecycle: [create - destroy] + private long nsOpenGLLayer = 0; // lifecycle: [associateDrawable_true - associateDrawable_false] private float screenVSyncTimeout; // microSec private int vsyncTimeout; // microSec - for nsOpenGLLayer mode private int lastWidth=0, lastHeight=0; // allowing to detect size change @@ -479,18 +472,6 @@ public abstract class MacOSXCGLContext extends GLContextImpl @Override public boolean isNSContext() { return true; } - @Override - public void drawableChangedNotify() { - backingLayerHost = NativeWindowFactory.getOffscreenLayerSurface(drawable.getNativeSurface(), true); - if( null == backingLayerHost ) { - boolean[] isPBuffer = { false }; - boolean[] isFBO = { false }; - CGL.setContextView(contextHandle, getNSViewHandle(isPBuffer, isFBO)); - } else { - nsOpenGLLayer = backingLayerHost.getAttachedSurfaceLayer(); - } - } - private long getNSViewHandle(boolean[] isPBuffer, boolean[] isFBO) { final long nsViewHandle; if(drawable instanceof GLFBODrawableImpl) { @@ -549,13 +530,12 @@ public abstract class MacOSXCGLContext extends GLContextImpl isPBuffer = _isPBuffer[0]; isFBO = _isFBO[0]; } - backingLayerHost = NativeWindowFactory.getOffscreenLayerSurface(surface, true); + final OffscreenLayerSurface backingLayerHost = NativeWindowFactory.getOffscreenLayerSurface(surface, true); boolean incompleteView = null != backingLayerHost; if( !incompleteView && surface instanceof ProxySurface ) { incompleteView = ((ProxySurface)surface).containsUpstreamOptionBits( ProxySurface.OPT_UPSTREAM_WINDOW_INVISIBLE ); } - long pixelFormat; { final GLCapabilitiesImmutable targetCaps; if( isFBO ) { @@ -606,48 +586,54 @@ public abstract class MacOSXCGLContext extends GLContextImpl } config.setChosenCapabilities(fixedCaps); - try { - final IntBuffer viewNotReady = Buffers.newDirectIntBuffer(1); - // Try to allocate a context with this - ctx = CGL.createContext(share, - nsViewHandle, incompleteView, - pixelFormat, - chosenCaps.isBackgroundOpaque(), - viewNotReady); - if (0 == ctx) { - if(DEBUG) { - System.err.println("NS create failed: viewNotReady: "+ (1 == viewNotReady.get(0))); - } - return 0; + final IntBuffer viewNotReady = Buffers.newDirectIntBuffer(1); + // Try to allocate a context with this + ctx = CGL.createContext(share, nsViewHandle, incompleteView, + pixelFormat, chosenCaps.isBackgroundOpaque(), viewNotReady); + if (0 == ctx) { + if(DEBUG) { + System.err.println("NS create failed: viewNotReady: "+ (1 == viewNotReady.get(0))); } + return 0; + } - if(null != backingLayerHost) { - nsOpenGLLayerPFmt = pixelFormat; - pixelFormat = 0; - } - - if (chosenCaps.isOnscreen() && !chosenCaps.isBackgroundOpaque()) { - // Set the context opacity - CGL.setContextOpacity(ctx, 0); - } - } finally { - if(0!=pixelFormat) { - CGL.deletePixelFormat(pixelFormat); - pixelFormat = 0; - } + if (chosenCaps.isOnscreen() && !chosenCaps.isBackgroundOpaque()) { + // Set the context opacity + CGL.setContextOpacity(ctx, 0); } return ctx; } @Override public boolean destroy(long ctx) { + if(0!=pixelFormat) { + CGL.deletePixelFormat(pixelFormat); + pixelFormat = 0; + } return CGL.deleteContext(ctx, true); + } @Override - public boolean contextRealized(boolean realized) { - if( realized ) { + public void associateDrawable(boolean bound) { + final OffscreenLayerSurface backingLayerHost = NativeWindowFactory.getOffscreenLayerSurface(drawable.getNativeSurface(), true); + + if(DEBUG) { + System.err.println("MaxOSXCGLContext.NSOpenGLImpl.associateDrawable: "+bound+", ctx "+toHexString(contextHandle)+", hasBackingLayerHost "+(null!=backingLayerHost)); + } + + if( bound ) { + if( null != backingLayerHost ) { + + if( 0 != nsOpenGLLayer ) { // FIXME: redundant + throw new InternalError("Lifecycle: bound=true, hasBackingLayerHost=true, but 'nsOpenGLLayer' is already/still set local: "+nsOpenGLLayer+", "+this); + } + nsOpenGLLayer = backingLayerHost.getAttachedSurfaceLayer(); + if( 0 != nsOpenGLLayer ) { // FIXME: redundant + throw new InternalError("Lifecycle: bound=true, hasBackingLayerHost=true, but 'nsOpenGLLayer' is already/still set on backingLayerHost: "+nsOpenGLLayer+", "+this); + } + // // handled layered surface // @@ -700,8 +686,7 @@ public abstract class MacOSXCGLContext extends GLContextImpl } final boolean ctxUnlocked = CGL.kCGLNoError == CGL.CGLUnlockContext(cglCtx); try { - nsOpenGLLayer = CGL.createNSOpenGLLayer(ctx, gl3ShaderProgramName, nsOpenGLLayerPFmt, pbufferHandle, texID, chosenCaps.isBackgroundOpaque(), lastWidth, lastHeight); - nsOpenGLLayerPFmt = 0; // NSOpenGLLayer will release pfmt + nsOpenGLLayer = CGL.createNSOpenGLLayer(ctx, gl3ShaderProgramName, pixelFormat, pbufferHandle, texID, chosenCaps.isBackgroundOpaque(), lastWidth, lastHeight); if (DEBUG) { System.err.println("NS create nsOpenGLLayer "+toHexString(nsOpenGLLayer)+" w/ pbuffer "+toHexString(pbufferHandle)+", texID "+texID+", texSize "+lastWidth+"x"+lastHeight+", "+drawable); } @@ -718,17 +703,22 @@ public abstract class MacOSXCGLContext extends GLContextImpl } else { lastWidth = drawable.getWidth(); lastHeight = drawable.getHeight(); + boolean[] isPBuffer = { false }; + boolean[] isFBO = { false }; + CGL.setContextView(contextHandle, getNSViewHandle(isPBuffer, isFBO)); } } else { if( 0 != nsOpenGLLayer ) { - final NativeSurface surface = drawable.getNativeSurface(); + if( null == backingLayerHost ) { // FIXME: redundant + throw new InternalError("Lifecycle: bound=false, hasNSOpneGLLayer=true, but 'backingLayerHost' is null local: "+nsOpenGLLayer+", "+this); + } + if (DEBUG) { System.err.println("NS destroy nsOpenGLLayer "+toHexString(nsOpenGLLayer)+", "+drawable); } - final OffscreenLayerSurface ols = NativeWindowFactory.getOffscreenLayerSurface(surface, true); - if(null != ols && ols.isSurfaceLayerAttached()) { + if( backingLayerHost.isSurfaceLayerAttached() ) { // still having a valid OLS attached to surface (parent OLS could have been removed) - ols.detachSurfaceLayer(); + backingLayerHost.detachSurfaceLayer(); } CGL.releaseNSOpenGLLayer(nsOpenGLLayer); if( null != gl3ShaderProgram ) { @@ -738,8 +728,6 @@ public abstract class MacOSXCGLContext extends GLContextImpl nsOpenGLLayer = 0; } } - backingLayerHost = null; - return true; } private final void validatePBufferConfig(long ctx) { @@ -889,11 +877,6 @@ public abstract class MacOSXCGLContext extends GLContextImpl public boolean isNSContext() { return false; } @Override - public void drawableChangedNotify() { - // FIXME - } - - @Override public long create(long share, int ctp, int major, int minor) { long ctx = 0; MacOSXCGLGraphicsConfiguration config = (MacOSXCGLGraphicsConfiguration) drawable.getNativeSurface().getGraphicsConfiguration(); @@ -947,8 +930,7 @@ public abstract class MacOSXCGLContext extends GLContextImpl } @Override - public boolean contextRealized(boolean realized) { - return true; + public void associateDrawable(boolean bound) { } @Override diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawable.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawable.java index ff1772860..0f282d33f 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawable.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawable.java @@ -119,7 +119,6 @@ public abstract class MacOSXCGLDrawable extends GLDrawableImpl { if(bound) { final MacOSXCGLContext osxCtx = (MacOSXCGLContext)ctx; createdContexts.add(new WeakReference<MacOSXCGLContext>(osxCtx)); - osxCtx.drawableChangedNotify(); } else { for(int i=0; i<createdContexts.size(); ) { final WeakReference<MacOSXCGLContext> ref = createdContexts.get(i); diff --git a/src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m b/src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m index 2cf74380c..abc9d7958 100644 --- a/src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m +++ b/src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m @@ -275,7 +275,7 @@ static const GLfloat gl_verts[] = { vboBufTexCoord = 0; vertAttrLoc = 0; texCoordAttrLoc = 0; - parentPixelFmt = _parentPixelFmt; + parentPixelFmt = [_parentPixelFmt retain]; // until destruction swapInterval = 1; // defaults to on (as w/ new GL profiles) swapIntervalCounter = 0; timespec_now(&lastWaitTime); @@ -338,13 +338,13 @@ static const GLfloat gl_verts[] = { #ifdef VERBOSE_ON CGRect lRect = [self bounds]; if(NULL != pbuffer) { - DBG_PRINT("MyNSOpenGLLayer::init (pbuffer) %p, ctx %p, pfmt %p, pbuffer %p, opaque %d, pbuffer %dx%d -> tex %dx%d, bounds: %lf/%lf %lfx%lf (refcnt %d)\n", + DBG_PRINT("MyNSOpenGLLayer::init (pbuffer) %p, ctx %p, pfmt %p, pbuffer %p, opaque %d, pbuffer %dx%d -> tex %dx%d, bounds: %lf/%lf %lfx%lf, displayLink %p (refcnt %d)\n", self, parentCtx, parentPixelFmt, pbuffer, opaque, [pbuffer pixelsWide], [pbuffer pixelsHigh], texWidth, texHeight, - lRect.origin.x, lRect.origin.y, lRect.size.width, lRect.size.height, (int)[self retainCount]); + lRect.origin.x, lRect.origin.y, lRect.size.width, lRect.size.height, displayLink, (int)[self retainCount]); } else { - DBG_PRINT("MyNSOpenGLLayer::init (texture) %p, ctx %p, pfmt %p, opaque %d, tex[id %d, %dx%d], bounds: %lf/%lf %lfx%lf (refcnt %d)\n", + DBG_PRINT("MyNSOpenGLLayer::init (texture) %p, ctx %p, pfmt %p, opaque %d, tex[id %d, %dx%d], bounds: %lf/%lf %lfx%lf, displayLink %p (refcnt %d)\n", self, parentCtx, parentPixelFmt, opaque, (int)textureID, texWidth, texHeight, - lRect.origin.x, lRect.origin.y, lRect.size.width, lRect.size.height, (int)[self retainCount]); + lRect.origin.x, lRect.origin.y, lRect.size.width, lRect.size.height, displayLink, (int)[self retainCount]); } #endif return self; @@ -498,7 +498,10 @@ static const GLfloat gl_verts[] = { myCtx = NULL; } parentCtx = NULL; - [parentPixelFmt release]; + if( NULL != parentPixelFmt ) { + [parentPixelFmt release]; + parentPixelFmt = NULL; + } pthread_mutex_unlock(&renderLock); [self release]; DBG_PRINT("MyNSOpenGLLayer::releaseLayer.X: %p\n", self); @@ -623,7 +626,7 @@ static const GLfloat gl_verts[] = { { DBG_PRINT("MyNSOpenGLLayer::openGLPixelFormatForDisplayMask: %p (refcnt %d) - parent-pfmt %p -> new-pfmt %p\n", self, (int)[self retainCount], parentPixelFmt, parentPixelFmt); - // We simply take over ownership of parent PixelFormat .. + // We simply take over ownership of parent PixelFormat until releaseLayer.. return parentPixelFmt; } diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/GLContextDrawableSwitchBase.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/GLContextDrawableSwitchBase.java new file mode 100644 index 000000000..f26a96a90 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/GLContextDrawableSwitchBase.java @@ -0,0 +1,276 @@ +/** + * Copyright 2013 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.test.junit.jogl.acore.glels; + +import java.awt.Dimension; +import java.awt.Frame; + +import javax.media.opengl.GLAnimatorControl; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLDrawableFactory; +import javax.media.opengl.GLOffscreenAutoDrawable; +import javax.media.opengl.GLProfile; +import javax.media.opengl.awt.GLCanvas; + +import jogamp.nativewindow.jawt.JAWTUtil; +import jogamp.opengl.GLEventListenerState; + +import com.jogamp.newt.opengl.GLWindow; + +import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; +import com.jogamp.opengl.test.junit.util.AWTRobotUtil; +import com.jogamp.opengl.test.junit.util.GLEventListenerCounter; +import com.jogamp.opengl.test.junit.util.UITestCase; + +import org.junit.Assert; +import org.junit.Assume; +import org.junit.BeforeClass; + +/** + * Test re-association of GLContext/GLDrawables, + * here GLContext's survival of GLDrawable destruction + * and reuse w/ new or recreated GLDrawable. + * <p> + * Test utilizes {@link GLEventListenerState} for preserving the + * GLAutoDrawable state, i.e. GLContext, all GLEventListener + * and the GLAnimatorControl association. + * </p> + * <p> + * See Bug 665 - https://jogamp.org/bugzilla/show_bug.cgi?id=665. + * </p> + */ +public abstract class GLContextDrawableSwitchBase extends UITestCase { + static protected enum GLADType { GLCanvasOnscreen, GLCanvasOffscreen, GLWindow, GLOffscreen }; + + // default period for 1 GLAD cycle + static long duration = 1000; // ms + + static int width, height; + + static GLCapabilities getCaps(String profile) { + if( !GLProfile.isAvailable(profile) ) { + System.err.println("Profile "+profile+" n/a"); + return null; + } + return new GLCapabilities(GLProfile.get(profile)); + } + + @BeforeClass + public static void initClass() { + width = 256; + height = 256; + } + + static void setGLCanvasSize(final GLCanvas glc, final Dimension new_sz) { + try { + javax.swing.SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + glc.setMinimumSize(new_sz); + glc.setPreferredSize(new_sz); + glc.setSize(new_sz); + } } ); + } catch( Throwable throwable ) { + throwable.printStackTrace(); + Assume.assumeNoException( throwable ); + } + } + + static void setFrameVisible(final Frame frame) throws InterruptedException { + try { + javax.swing.SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + frame.pack(); + frame.setVisible(true); + }}); + } catch( Throwable throwable ) { + throwable.printStackTrace(); + Assume.assumeNoException( throwable ); + } + } + + static void destroyFrame(final Frame frame) throws InterruptedException { + try { + javax.swing.SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + frame.dispose(); + }}); + } catch( Throwable throwable ) { + throwable.printStackTrace(); + Assume.assumeNoException( throwable ); + } + } + + private GLOffscreenAutoDrawable createGLOffscreenAutoDrawable(GLCapabilities caps, int width, int height) throws InterruptedException { + final GLDrawableFactory factory = GLDrawableFactory.getFactory(caps.getGLProfile()); + return factory.createOffscreenAutoDrawable(null, caps, null, width, height, null); + } + + protected static boolean validateOnOffscreenLayer(GLADType gladType1, GLADType gladType2) { + final boolean useOffscreenLayer = GLADType.GLCanvasOffscreen == gladType1 || GLADType.GLCanvasOffscreen == gladType2 ; + final boolean useOnscreenLayer = GLADType.GLCanvasOnscreen == gladType1 || GLADType.GLCanvasOnscreen == gladType2 ; + if( useOffscreenLayer ) { + if( !JAWTUtil.isOffscreenLayerSupported() ) { + System.err.println("Platform doesn't support offscreen rendering."); + return false; + } + } else if( useOnscreenLayer ) { + if( JAWTUtil.isOffscreenLayerRequired() ) { + System.err.println("Platform requires offscreen rendering."); + return false; + } + } + return true; + } + + protected void testGLADOneLifecycle(GLCapabilities caps, GLADType gladType, int width, int height, + GLEventListenerCounter glelTracker, SnapshotGLEventListener snapshotGLEventListener, + GLEventListenerState glelsIn, GLEventListenerState glelsOut[], GLAnimatorControl animator) + throws InterruptedException { + + final Frame frame; + final GLAutoDrawable glad; + if( GLADType.GLCanvasOnscreen == gladType ) { + if( jogamp.nativewindow.jawt.JAWTUtil.isOffscreenLayerRequired() ) { + throw new InternalError("Platform requires offscreen rendering, but onscreen requested: "+gladType); + } + frame = new Frame("AWT GLCanvas"); + + glad = new GLCanvas(caps); + setGLCanvasSize((GLCanvas)glad, new Dimension(width, height)); + frame.add((GLCanvas)glad); + } else if( GLADType.GLCanvasOffscreen == gladType ) { + if( !jogamp.nativewindow.jawt.JAWTUtil.isOffscreenLayerSupported() ) { + throw new InternalError("Platform doesn't support offscreen rendering: "+gladType); + } + frame = new Frame("AWT GLCanvas"); + + glad = new GLCanvas(caps); + ((GLCanvas)glad).setShallUseOffscreenLayer(true); + setGLCanvasSize((GLCanvas)glad, new Dimension(width, height)); + frame.add((GLCanvas)glad); + } else if( GLADType.GLWindow == gladType ) { + frame = null; + + glad = GLWindow.create(caps); + ((GLWindow)glad).setTitle("Newt GLWindow"); + ((GLWindow)glad).setSize(width, height); + } else if( GLADType.GLOffscreen == gladType ) { + frame = null; + + glad = this.createGLOffscreenAutoDrawable(caps, width, height); + } else { + throw new InternalError("Unsupported: "+gladType); + } + + if( null == glelsIn ) { + if( null != animator ) { + animator.add(glad); + } + glad.addGLEventListener(glelTracker); + glad.addGLEventListener(new GearsES2(1)); + glad.addGLEventListener(snapshotGLEventListener); + } + snapshotGLEventListener.setMakeSnapshot(); + + if( GLADType.GLCanvasOnscreen == gladType || GLADType.GLCanvasOffscreen == gladType ) { + setFrameVisible(frame); + Assert.assertEquals(true, AWTRobotUtil.waitForVisible(frame, true)); + } else if( GLADType.GLWindow == gladType ) { + ((GLWindow)glad).setVisible(true); + } + Assert.assertEquals(true, AWTRobotUtil.waitForRealized(glad, true)); + Assert.assertNotNull(glad.getContext()); + Assert.assertTrue(glad.isRealized()); + + if( null != glelsIn ) { + Assert.assertEquals(0, glad.getGLEventListenerCount()); + glelsIn.moveTo(glad); + + Assert.assertEquals(1, glelTracker.initCount); + Assert.assertTrue(1 <= glelTracker.reshapeCount); + Assert.assertTrue(1 <= glelTracker.displayCount); + Assert.assertEquals(0, glelTracker.disposeCount); + Assert.assertEquals(3, glad.getGLEventListenerCount()); + + Assert.assertEquals(glelsIn.context, glad.getContext()); + Assert.assertEquals(glelsIn.listenerCount(), glad.getGLEventListenerCount()); + Assert.assertEquals(glelsIn.context.getGLReadDrawable(), glad.getDelegatedDrawable()); + Assert.assertEquals(glelsIn.context.getGLDrawable(), glad.getDelegatedDrawable()); + Assert.assertEquals(false, glelsIn.isOwner()); + } + + final long t0 = System.currentTimeMillis(); + long t1 = t0; + + while( ( t1 - t0 ) < duration ) { + Thread.sleep(100); + t1 = System.currentTimeMillis(); + } + + Assert.assertEquals(1, glelTracker.initCount); + Assert.assertTrue(1 <= glelTracker.reshapeCount); + Assert.assertTrue(1 <= glelTracker.displayCount); + Assert.assertEquals(0, glelTracker.disposeCount); + + if( null != glelsOut ) { + final GLContext context1 = glad.getContext(); + final GLEventListenerState _gllsOut = GLEventListenerState.moveFrom(glad); + + Assert.assertEquals(context1, _gllsOut.context); + Assert.assertNull(context1.getGLReadDrawable()); + Assert.assertNull(context1.getGLDrawable()); + Assert.assertEquals(3, _gllsOut.listenerCount()); + Assert.assertEquals(true, _gllsOut.isOwner()); + Assert.assertEquals(null, glad.getContext()); + Assert.assertEquals(0, glad.getGLEventListenerCount()); + + glelsOut[0] = _gllsOut; + } + if( GLADType.GLCanvasOnscreen == gladType || GLADType.GLCanvasOffscreen == gladType ) { + destroyFrame(frame); + Assert.assertEquals(true, AWTRobotUtil.waitForVisible(frame, false)); + } else if( GLADType.GLWindow == gladType ) { + glad.destroy(); + } else if( GLADType.GLOffscreen == gladType ) { + glad.destroy(); + } + Assert.assertEquals(true, AWTRobotUtil.waitForRealized(glad, false)); + + Assert.assertEquals(1, glelTracker.initCount); + Assert.assertTrue(1 <= glelTracker.reshapeCount); + Assert.assertTrue(1 <= glelTracker.displayCount); + if( null != glelsOut ) { + Assert.assertEquals(0, glelTracker.disposeCount); + } else { + Assert.assertEquals(1, glelTracker.disposeCount); + } + } +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextDrawableSwitch01NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch01NEWT.java index cce4149ba..5f2b9fb53 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextDrawableSwitch01NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch01NEWT.java @@ -26,7 +26,7 @@ * or implied, of JogAmp Community. */ -package com.jogamp.opengl.test.junit.jogl.acore; +package com.jogamp.opengl.test.junit.jogl.acore.glels; import java.io.IOException; @@ -153,9 +153,9 @@ public class TestGLContextDrawableSwitch01NEWT extends UITestCase { { final GLContext newCtx = glad1.createContext(null); Assert.assertNotNull(newCtx); - final GLContext oldCtx = glad1.setContext(newCtx); + final GLContext oldCtx = glad1.setContext(newCtx, true); Assert.assertNotNull(oldCtx); - oldCtx.destroy(); + Assert.assertFalse(oldCtx.isCreated()); final int res = newCtx.makeCurrent(); Assert.assertTrue(GLContext.CONTEXT_CURRENT_NEW==res || GLContext.CONTEXT_CURRENT==res); newCtx.release(); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextDrawableSwitch11NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch10NEWT.java index 2ef797fba..a1611992e 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextDrawableSwitch11NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch10NEWT.java @@ -26,7 +26,7 @@ * or implied, of JogAmp Community. */ -package com.jogamp.opengl.test.junit.jogl.acore; +package com.jogamp.opengl.test.junit.jogl.acore.glels; import java.io.IOException; @@ -36,7 +36,6 @@ import com.jogamp.newt.event.WindowAdapter; import com.jogamp.newt.event.WindowEvent; import com.jogamp.newt.event.WindowListener; import com.jogamp.newt.event.WindowUpdateEvent; -import com.jogamp.newt.opengl.GLWindow; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLCapabilities; @@ -62,13 +61,24 @@ import org.junit.Test; /** * Test re-association of GLContext/GLDrawables, - * here GLContext's survival of GLDrawable desctruction + * here GLContext's survival of GLDrawable destruction * and reuse w/ new or recreated GLDrawable. * <p> + * Test utilizes {@link GLEventListenerState} for preserving the + * GLAutoDrawable state, i.e. GLContext, all GLEventListener + * and the GLAnimatorControl association. + * </p> + * <p> + * This test is using NEWT's plain Window w/ GLAutoDrawableDelegate. + * </p> + * <p> * See Bug 665 - https://jogamp.org/bugzilla/show_bug.cgi?id=665. * </p> */ -public class TestGLContextDrawableSwitch11NEWT extends UITestCase { +public class TestGLContextDrawableSwitch10NEWT extends UITestCase { + // default period for 1 GLAD cycle + static long duration = 1000; // ms + static int width, height; static GLCapabilities getCaps(String profile) { @@ -134,33 +144,31 @@ public class TestGLContextDrawableSwitch11NEWT extends UITestCase { public void test01GLADDelegateGL2ES2() throws InterruptedException { final GLCapabilities reqGLCaps = getCaps(GLProfile.GL2ES2); if(null == reqGLCaps) return; - test01GLADDelegateImpl(reqGLCaps); + testGLADDelegateImpl(reqGLCaps); } @Test(timeout=30000) - public void test01GLADDelegateGLES2() throws InterruptedException { + public void test02GLADDelegateGLES2() throws InterruptedException { final GLCapabilities reqGLCaps = getCaps(GLProfile.GLES2); if(null == reqGLCaps) return; - test01GLADDelegateImpl(reqGLCaps); + testGLADDelegateImpl(reqGLCaps); } - private void test01GLADDelegateImpl(GLCapabilities caps) throws InterruptedException { - final QuitAdapter quitAdapter = new QuitAdapter(); - + private void testGLADDelegateImpl(GLCapabilities caps) throws InterruptedException { final GLEventListenerCounter glelCounter = new GLEventListenerCounter(); final SnapshotGLEventListener snapshotGLEventListener = new SnapshotGLEventListener(); final Animator animator = new Animator(); animator.start(); - final long t0 = System.currentTimeMillis(); final GLEventListenerState glls1; // - create glad1 w/o context // - create context using glad1 and assign it to glad1 { + final QuitAdapter quitAdapter = new QuitAdapter(); final GLAutoDrawable glad1 = createGLAutoDrawableWithoutContext(caps, 64, 64, width, height, quitAdapter); final GLContext context1 = glad1.createContext(null); - glad1.setContext(context1); + glad1.setContext(context1, true); animator.add(glad1); glad1.addGLEventListener(glelCounter); @@ -168,9 +176,10 @@ public class TestGLContextDrawableSwitch11NEWT extends UITestCase { glad1.addGLEventListener(snapshotGLEventListener); snapshotGLEventListener.setMakeSnapshot(); - long t1 = System.currentTimeMillis(); + final long t0 = System.currentTimeMillis(); + long t1 = t0; - while( !quitAdapter.shouldQuit() && ( t1 - t0 ) < duration/2 ) { + while( !quitAdapter.shouldQuit() && ( t1 - t0 ) < duration ) { Thread.sleep(100); t1 = System.currentTimeMillis(); } @@ -209,6 +218,7 @@ public class TestGLContextDrawableSwitch11NEWT extends UITestCase { // - create glad2 w/ survived context { + final QuitAdapter quitAdapter = new QuitAdapter(); final GLAutoDrawable glad2 = createGLAutoDrawableWithoutContext(caps, 2*64+width, 64, width+100, height+100, quitAdapter); snapshotGLEventListener.setMakeSnapshot(); @@ -229,9 +239,10 @@ public class TestGLContextDrawableSwitch11NEWT extends UITestCase { Assert.assertEquals(glls1.context.getGLDrawable(), glad2.getDelegatedDrawable()); Assert.assertEquals(false, glls1.isOwner()); - long t1 = System.currentTimeMillis(); + final long t0 = System.currentTimeMillis(); + long t1 = t0; - while( !quitAdapter.shouldQuit() && ( t1 - t0 ) < duration/1 ) { + while( !quitAdapter.shouldQuit() && ( t1 - t0 ) < duration ) { Thread.sleep(100); t1 = System.currentTimeMillis(); } @@ -245,129 +256,6 @@ public class TestGLContextDrawableSwitch11NEWT extends UITestCase { animator.stop(); } - @Test(timeout=30000) - public void test02GLWindowGL2ES2() throws InterruptedException { - final GLCapabilities reqGLCaps = getCaps(GLProfile.GL2ES2); - if(null == reqGLCaps) return; - test02GLWindowImpl(reqGLCaps); - } - - @Test(timeout=30000) - public void test02GLWindowGLES2() throws InterruptedException { - final GLCapabilities reqGLCaps = getCaps(GLProfile.GLES2); - if(null == reqGLCaps) return; - test02GLWindowImpl(reqGLCaps); - } - - private void test02GLWindowImpl(GLCapabilities caps) throws InterruptedException { - final QuitAdapter quitAdapter = new QuitAdapter(); - - final SnapshotGLEventListener snapshotGLEventListener = new SnapshotGLEventListener(); - final GLEventListenerCounter glelTracker = new GLEventListenerCounter(); - final Animator animator = new Animator(); - animator.start(); - - final long t0 = System.currentTimeMillis(); - final GLEventListenerState glls1; - - // - create glad1 w/o context - // - create context using glad1 and assign it to glad1 - { - final GLWindow glad1 = GLWindow.create(caps); - glad1.setSize(width, height); - glad1.setPosition(64, 64); - glad1.addWindowListener(quitAdapter); - animator.add(glad1); - - glad1.addGLEventListener(glelTracker); - glad1.addGLEventListener(new GearsES2(1)); - glad1.addGLEventListener(snapshotGLEventListener); - snapshotGLEventListener.setMakeSnapshot(); - - glad1.setVisible(true); - - long t1 = System.currentTimeMillis(); - - while( !quitAdapter.shouldQuit() && ( t1 - t0 ) < duration/2 ) { - Thread.sleep(100); - t1 = System.currentTimeMillis(); - } - - // - dis-associate context from glad1 - // - destroy glad1 - Assert.assertEquals(1, glelTracker.initCount); - Assert.assertTrue(1 <= glelTracker.reshapeCount); - Assert.assertTrue(1 <= glelTracker.displayCount); - Assert.assertEquals(0, glelTracker.disposeCount); - Assert.assertEquals(3, glad1.getGLEventListenerCount()); - Assert.assertEquals(glad1.getContext().getGLReadDrawable(), glad1.getDelegatedDrawable()); - Assert.assertEquals(glad1.getContext().getGLDrawable(), glad1.getDelegatedDrawable()); - - final GLContext context1 = glad1.getContext(); - glls1 = GLEventListenerState.moveFrom(glad1); - - Assert.assertEquals(1, glelTracker.initCount); - Assert.assertTrue(1 <= glelTracker.reshapeCount); - Assert.assertTrue(1 <= glelTracker.displayCount); - Assert.assertEquals(0, glelTracker.disposeCount); - Assert.assertEquals(context1, glls1.context); - Assert.assertNull(context1.getGLReadDrawable()); - Assert.assertNull(context1.getGLDrawable()); - Assert.assertEquals(3, glls1.listenerCount()); - Assert.assertEquals(true, glls1.isOwner()); - Assert.assertEquals(null, glad1.getContext()); - Assert.assertEquals(0, glad1.getGLEventListenerCount()); - - glad1.destroy(); - Assert.assertEquals(1, glelTracker.initCount); - Assert.assertTrue(1 <= glelTracker.reshapeCount); - Assert.assertTrue(1 <= glelTracker.displayCount); - Assert.assertEquals(0, glelTracker.disposeCount); - } - - // - create glad2 w/ survived context - { - final GLWindow glad2 = GLWindow.create(caps); - glad2.setSize(width+100, height+100); - glad2.setPosition(2*64+width, 64); - glad2.addWindowListener(quitAdapter); - snapshotGLEventListener.setMakeSnapshot(); - glad2.setVisible(true); - - Assert.assertNotNull(glad2.getContext()); - Assert.assertEquals(0, glad2.getGLEventListenerCount()); - - glls1.moveTo(glad2); - - Assert.assertEquals(1, glelTracker.initCount); - Assert.assertTrue(1 <= glelTracker.reshapeCount); - Assert.assertTrue(1 <= glelTracker.displayCount); - Assert.assertEquals(0, glelTracker.disposeCount); - Assert.assertEquals(glls1.context, glad2.getContext()); - Assert.assertEquals(3, glad2.getGLEventListenerCount()); - Assert.assertEquals(glls1.context.getGLReadDrawable(), glad2.getDelegatedDrawable()); - Assert.assertEquals(glls1.context.getGLDrawable(), glad2.getDelegatedDrawable()); - Assert.assertEquals(false, glls1.isOwner()); - - long t1 = System.currentTimeMillis(); - - while( !quitAdapter.shouldQuit() && ( t1 - t0 ) < duration/1 ) { - Thread.sleep(100); - t1 = System.currentTimeMillis(); - } - - glad2.destroy(); - Assert.assertEquals(1, glelTracker.initCount); - Assert.assertTrue(1 <= glelTracker.reshapeCount); - Assert.assertTrue(1 <= glelTracker.displayCount); - Assert.assertEquals(1, glelTracker.disposeCount); - } - animator.stop(); - } - - // default timing for 2 switches - static long duration = 2200; // ms - public static void main(String args[]) throws IOException { for(int i=0; i<args.length; i++) { if(args[i].equals("-time")) { @@ -381,6 +269,6 @@ public class TestGLContextDrawableSwitch11NEWT extends UITestCase { BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); System.err.println("Press enter to continue"); System.err.println(stdin.readLine()); */ - org.junit.runner.JUnitCore.main(TestGLContextDrawableSwitch11NEWT.class.getName()); + org.junit.runner.JUnitCore.main(TestGLContextDrawableSwitch10NEWT.class.getName()); } } diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch11NewtAWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch11NewtAWT.java new file mode 100644 index 000000000..9ad89c4be --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch11NewtAWT.java @@ -0,0 +1,118 @@ +/** + * Copyright 2013 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.test.junit.jogl.acore.glels; + +import java.io.IOException; + +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLProfile; + +import jogamp.opengl.GLEventListenerState; + +import com.jogamp.opengl.util.Animator; + +import com.jogamp.opengl.test.junit.util.GLEventListenerCounter; + +import org.junit.Test; + +/** + * Test re-association of GLContext/GLDrawables, + * here GLContext's survival of GLDrawable destruction + * and reuse w/ new or recreated GLDrawable. + * <p> + * Test utilizes {@link GLEventListenerState} for preserving the + * GLAutoDrawable state, i.e. GLContext, all GLEventListener + * and the GLAnimatorControl association. + * </p> + * <p> + * This test is using JOGL's NEWT GLWindow. + * </p> + * <p> + * See Bug 665 - https://jogamp.org/bugzilla/show_bug.cgi?id=665. + * </p> + */ +public class TestGLContextDrawableSwitch11NewtAWT extends GLContextDrawableSwitchBase { + + @Test(timeout=30000) + public void test21GLWindowGL2ES2() throws InterruptedException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GL2ES2); + if(null == reqGLCaps) return; + testGLWindowImpl(reqGLCaps); + } + + @Test(timeout=30000) + public void test22GLWindowGLES2() throws InterruptedException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GLES2); + if(null == reqGLCaps) return; + testGLWindowImpl(reqGLCaps); + } + + private void testGLWindowImpl(GLCapabilities caps) throws InterruptedException { + final SnapshotGLEventListener snapshotGLEventListener = new SnapshotGLEventListener(); + final GLEventListenerCounter glelTracker = new GLEventListenerCounter(); + final Animator animator = new Animator(); + animator.start(); + + final GLEventListenerState glels[] = new GLEventListenerState[1]; + + // - create glad1 w/o context + // - create context using glad1 and assign it to glad1 + { + testGLADOneLifecycle(caps, GLADType.GLWindow, width, height, + glelTracker, snapshotGLEventListener, + null, + glels, animator); + } + + // - create glad2 w/ survived context + { + testGLADOneLifecycle(caps, GLADType.GLWindow, width+100, height+100, + glelTracker, snapshotGLEventListener, + glels[0], + null, null); + } + animator.stop(); + } + + public static void main(String args[]) throws IOException { + for(int i=0; i<args.length; i++) { + if(args[i].equals("-time")) { + i++; + try { + duration = Integer.parseInt(args[i]); + } catch (Exception ex) { ex.printStackTrace(); } + } + } + /** + BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); + System.err.println("Press enter to continue"); + System.err.println(stdin.readLine()); */ + org.junit.runner.JUnitCore.main(TestGLContextDrawableSwitch11NewtAWT.class.getName()); + } +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch12AWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch12AWT.java new file mode 100644 index 000000000..aa75fa1b1 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch12AWT.java @@ -0,0 +1,144 @@ +/** + * Copyright 2013 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.test.junit.jogl.acore.glels; + +import java.io.IOException; + +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLProfile; + +import jogamp.nativewindow.jawt.JAWTUtil; +import jogamp.opengl.GLEventListenerState; + +import com.jogamp.opengl.util.Animator; + +import com.jogamp.opengl.test.junit.util.GLEventListenerCounter; + +import org.junit.Test; + +/** + * Test re-association of GLContext/GLDrawables, + * here GLContext's survival of GLDrawable destruction + * and reuse w/ new or recreated GLDrawable. + * <p> + * Test utilizes {@link GLEventListenerState} for preserving the + * GLAutoDrawable state, i.e. GLContext, all GLEventListener + * and the GLAnimatorControl association. + * </p> + * <p> + * This test is using JOGL's AWT GLCanvas + * </p> + * <p> + * See Bug 665 - https://jogamp.org/bugzilla/show_bug.cgi?id=665. + * </p> + */ +public class TestGLContextDrawableSwitch12AWT extends GLContextDrawableSwitchBase { + + @Test(timeout=30000) + public void test01GLCanvasOnscreenGL2ES2() throws InterruptedException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GL2ES2); + if(null == reqGLCaps) return; + testGLCanvasImpl(reqGLCaps, false); + } + + @Test(timeout=30000) + public void test02GLCanvasOnscreenGLES2() throws InterruptedException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GLES2); + if(null == reqGLCaps) return; + testGLCanvasImpl(reqGLCaps, false); + } + + @Test(timeout=30000) + public void test11GLCanvasOffscreenGL2ES2() throws InterruptedException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GL2ES2); + if(null == reqGLCaps) return; + testGLCanvasImpl(reqGLCaps, true); + } + + @Test(timeout=30000) + public void test12GLCanvasOffscreenGLES2() throws InterruptedException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GLES2); + if(null == reqGLCaps) return; + testGLCanvasImpl(reqGLCaps, true); + } + + private void testGLCanvasImpl(GLCapabilities caps, boolean offscreenLayer) throws InterruptedException { + if( offscreenLayer ) { + if( !JAWTUtil.isOffscreenLayerSupported() ) { + System.err.println("Platform doesn't support offscreen rendering."); + return; + } + } else { + if( JAWTUtil.isOffscreenLayerRequired() ) { + System.err.println("Platform requires offscreen rendering."); + return; + } + } + final SnapshotGLEventListener snapshotGLEventListener = new SnapshotGLEventListener(); + final GLEventListenerCounter glelTracker = new GLEventListenerCounter(); + final Animator animator = new Animator(); + animator.start(); + + final GLEventListenerState glels[] = new GLEventListenerState[1]; + + // - create glad1 w/o context + // - create context using glad1 and assign it to glad1 + { + testGLADOneLifecycle(caps, offscreenLayer ? GLADType.GLCanvasOffscreen : GLADType.GLCanvasOnscreen, width, height, + glelTracker, snapshotGLEventListener, + null, + glels, animator); + } + + // - create glad2 w/ survived context + { + testGLADOneLifecycle(caps, offscreenLayer ? GLADType.GLCanvasOffscreen : GLADType.GLCanvasOnscreen, width+100, height+100, + glelTracker, snapshotGLEventListener, + glels[0], + null, null); + } + animator.stop(); + } + + public static void main(String args[]) throws IOException { + for(int i=0; i<args.length; i++) { + if(args[i].equals("-time")) { + i++; + try { + duration = Integer.parseInt(args[i]); + } catch (Exception ex) { ex.printStackTrace(); } + } + } + /** + BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); + System.err.println("Press enter to continue"); + System.err.println(stdin.readLine()); */ + org.junit.runner.JUnitCore.main(TestGLContextDrawableSwitch12AWT.class.getName()); + } +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch21Newt2AWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch21Newt2AWT.java new file mode 100644 index 000000000..d56054886 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch21Newt2AWT.java @@ -0,0 +1,165 @@ +/** + * Copyright 2013 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.test.junit.jogl.acore.glels; + +import java.io.IOException; + +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLProfile; + +import jogamp.opengl.GLEventListenerState; + +import com.jogamp.opengl.util.Animator; + +import com.jogamp.opengl.test.junit.util.GLEventListenerCounter; + +import org.junit.Test; + +/** + * Test re-association of GLContext/GLDrawables, + * here GLContext's survival of GLDrawable destruction + * and reuse w/ new or recreated GLDrawable. + * <p> + * Test utilizes {@link GLEventListenerState} for preserving the + * GLAutoDrawable state, i.e. GLContext, all GLEventListener + * and the GLAnimatorControl association. + * </p> + * <p> + * This test moves the {@link GLEventListenerState} from a + * NEWT GLWindow before it's destruction to an AWT GLCanvas after it's creation + * and vice versa + * </p> + * <p> + * See Bug 665 - https://jogamp.org/bugzilla/show_bug.cgi?id=665. + * </p> + */ +public class TestGLContextDrawableSwitch21Newt2AWT extends GLContextDrawableSwitchBase { + + @Test(timeout=30000) + public void test01GLCanvasOnScrn2GLWindowGL2ES2() throws InterruptedException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GL2ES2); + if(null == reqGLCaps) return; + testGLCanvas2GLWindowImpl(reqGLCaps, GLADType.GLCanvasOnscreen, GLADType.GLWindow); + } + + @Test(timeout=30000) + public void test02GLCanvasOnScrn2GLWindowGLES2() throws InterruptedException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GLES2); + if(null == reqGLCaps) return; + testGLCanvas2GLWindowImpl(reqGLCaps, GLADType.GLCanvasOnscreen, GLADType.GLWindow); + } + + @Test(timeout=30000) + public void test11GLWindow2GLCanvasOnScrnGL2ES2() throws InterruptedException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GL2ES2); + if(null == reqGLCaps) return; + testGLCanvas2GLWindowImpl(reqGLCaps, GLADType.GLWindow, GLADType.GLCanvasOnscreen); + } + + @Test(timeout=30000) + public void test12GLWindow2GLCanvasOnScrnGLES2() throws InterruptedException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GLES2); + if(null == reqGLCaps) return; + testGLCanvas2GLWindowImpl(reqGLCaps, GLADType.GLWindow, GLADType.GLCanvasOnscreen); + } + + @Test(timeout=30000) + public void test21GLCanvasOffScrn2GLWindowGL2ES2() throws InterruptedException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GL2ES2); + if(null == reqGLCaps) return; + testGLCanvas2GLWindowImpl(reqGLCaps, GLADType.GLCanvasOffscreen, GLADType.GLWindow); + } + + @Test(timeout=30000) + public void test22GLCanvasOffScrn2GLWindowGLES2() throws InterruptedException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GLES2); + if(null == reqGLCaps) return; + testGLCanvas2GLWindowImpl(reqGLCaps, GLADType.GLCanvasOffscreen, GLADType.GLWindow); + } + + @Test(timeout=30000) + public void test31GLWindow2GLCanvasOffScrnGL2ES2() throws InterruptedException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GL2ES2); + if(null == reqGLCaps) return; + testGLCanvas2GLWindowImpl(reqGLCaps, GLADType.GLWindow, GLADType.GLCanvasOffscreen); + } + + @Test(timeout=30000) + public void test32GLWindow2GLCanvasOffScrnGLES2() throws InterruptedException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GLES2); + if(null == reqGLCaps) return; + testGLCanvas2GLWindowImpl(reqGLCaps, GLADType.GLWindow, GLADType.GLCanvasOffscreen); + } + + private void testGLCanvas2GLWindowImpl(GLCapabilities caps, GLADType gladType1, GLADType gladType2) throws InterruptedException { + if( !validateOnOffscreenLayer(gladType1, gladType2) ) { + return; + } + final SnapshotGLEventListener snapshotGLEventListener = new SnapshotGLEventListener(); + final GLEventListenerCounter glelTracker = new GLEventListenerCounter(); + final Animator animator = new Animator(); + animator.start(); + + final GLEventListenerState glels[] = new GLEventListenerState[1]; + + // - create glad1 w/o context + // - create context using glad1 and assign it to glad1 + { + testGLADOneLifecycle(caps, gladType1, width, height, + glelTracker, snapshotGLEventListener, + null, + glels, animator); + } + + // - create glad2 w/ survived context + { + testGLADOneLifecycle(caps, gladType2, width+100, height+100, + glelTracker, snapshotGLEventListener, + glels[0], + null, null); + } + animator.stop(); + } + + public static void main(String args[]) throws IOException { + for(int i=0; i<args.length; i++) { + if(args[i].equals("-time")) { + i++; + try { + duration = Integer.parseInt(args[i]); + } catch (Exception ex) { ex.printStackTrace(); } + } + } + /** + BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); + System.err.println("Press enter to continue"); + System.err.println(stdin.readLine()); */ + org.junit.runner.JUnitCore.main(TestGLContextDrawableSwitch21Newt2AWT.class.getName()); + } +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NewtCanvasAWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NewtCanvasAWT.java index ad2165301..fd99b4f61 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NewtCanvasAWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NewtCanvasAWT.java @@ -57,6 +57,7 @@ import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; import javax.media.nativewindow.util.Dimension; +import javax.media.nativewindow.util.InsetsImmutable; import javax.media.nativewindow.util.Point; import javax.media.nativewindow.util.PointImmutable; import javax.media.nativewindow.util.DimensionImmutable; @@ -247,7 +248,7 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { animator.setModeBits(false, Animator.MODE_EXPECT_AWT_RENDERING_THREAD); animator.setExclusiveContext(exclusiveContext); - QuitAdapter quitAdapter = new QuitAdapter(); + final QuitAdapter quitAdapter = new QuitAdapter(); //glWindow.addKeyListener(new TraceKeyAdapter(quitAdapter)); //glWindow.addWindowListener(new TraceWindowAdapter(quitAdapter)); glWindow.addKeyListener(quitAdapter); @@ -265,6 +266,7 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { glWindow.addKeyListener(new KeyAdapter() { public void keyTyped(KeyEvent e) { if(e.getKeyChar()=='f') { + quitAdapter.enable(false); new Thread() { public void run() { final Thread t = glWindow.setExclusiveContextThread(null); @@ -272,7 +274,32 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { glWindow.setFullscreen(!glWindow.isFullscreen()); System.err.println("[set fullscreen post]: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getWidth()+"x"+glWindow.getHeight()+", f "+glWindow.isFullscreen()+", a "+glWindow.isAlwaysOnTop()+", "+glWindow.getInsets()); glWindow.setExclusiveContextThread(t); + quitAdapter.clear(); + quitAdapter.enable(true); } }.start(); + } else if(e.getKeyChar()=='r') { + quitAdapter.enable(false); + if(glWindow.getParent()==null) { + System.err.println("XXX glWin to home"); + glWindow.reparentWindow(newtCanvasAWT.getNativeWindow()); + } else { + final InsetsImmutable nInsets = glWindow.getInsets(); + java.awt.Insets aInsets = frame.getInsets(); + System.err.println("XXX glWin to TOP - insets " + nInsets + ", " + aInsets); + glWindow.reparentWindow(null); + int dx, dy; + if(nInsets.getTotalHeight()==0) { + dx = aInsets.left; + dy = aInsets.top; + } else { + dx = nInsets.getLeftWidth(); + dy = nInsets.getTopHeight(); + } + glWindow.setPosition(frame.getX()+frame.getWidth()+dx, frame.getY()+dy); + } + glWindow.requestFocus(); + quitAdapter.clear(); + quitAdapter.enable(true); } } }); @@ -308,7 +335,7 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { System.err.println("window resize "+rwsize+" -> pos/siz: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getWidth()+"x"+glWindow.getHeight()+", "+glWindow.getInsets()); } - while(!quitAdapter.shouldQuit() && animator.isAnimating() && animator.getTotalFPSDuration()<duration) { + while(!quitAdapter.shouldQuit() && animator.getTotalFPSDuration()<duration) { Thread.sleep(100); } diff --git a/src/test/com/jogamp/opengl/test/junit/util/QuitAdapter.java b/src/test/com/jogamp/opengl/test/junit/util/QuitAdapter.java index 42d68dadb..59e243171 100644 --- a/src/test/com/jogamp/opengl/test/junit/util/QuitAdapter.java +++ b/src/test/com/jogamp/opengl/test/junit/util/QuitAdapter.java @@ -32,18 +32,27 @@ import com.jogamp.newt.event.*; public class QuitAdapter extends WindowAdapter implements WindowListener, KeyListener { boolean shouldQuit = false; + boolean enabled = true; + public void enable(boolean v) { enabled = v; } + + public void clear() { shouldQuit = false; } + public boolean shouldQuit() { return shouldQuit; } public void windowDestroyNotify(WindowEvent e) { - System.err.println("QUIT Window "+Thread.currentThread()); - shouldQuit = true; + if( enabled ) { + System.err.println("QUIT Window "+Thread.currentThread()); + shouldQuit = true; + } } public void keyTyped(KeyEvent e) { - if(e.getKeyChar()=='q') { - System.err.println("QUIT Key "+Thread.currentThread()); - shouldQuit = true; + if( enabled ) { + if(e.getKeyChar()=='q') { + System.err.println("QUIT Key "+Thread.currentThread()); + shouldQuit = true; + } } } public void keyPressed(KeyEvent e) {} |