From b738983638703bb721ee4c9820c8ef43e2252e73 Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Thu, 24 Jan 2013 17:24:22 +0100 Subject: Bug 665 (part 1) - Allow dis-association of GLContext's GLDrawable .. Changes allowing re-association (incl. null) of GLContext/GLDrawable: - GLAutoDrawable: Refine API doc 'setContext(..)' - GLContext: Refine API doc: 'setGLDrawable(..)' 'getGLDrawable()' - GLContextImpl.setGLDrawable(): Handle null drawable - GLAutoDrawableDelegate/GLAutoDrawableBase: Allow null GLContext - GLDrawableHelper.switchContext(..)/recreateGLDrawable(): Balance GLContext.setGLDrawable(..) calls - New GLEventListenerState, holding state vector [GLEventListener, GLContext, .. ] impl. relocation of all components from/to GLAutoDrawable. - GLDrawableUtil - Using GLEventListenerState for swapGLContextAndAllGLEventListener(..) +++ NEWT Window*: - getDisplayHandle() is 'final', no more 'shortcut' code allowed due to re-association incl. display handle. - close*: - close config's device (was missing) - null config +++ Changes allowing reconfig of Display handle as required to re-associate pre-existing GLContext to a 'window': - AbstractGraphicsDevice: Add isHandleOwner() / clearHandleOwner() - Impl. in X11GraphicsDevice and EGLGraphicsDevice, NOP in DefaultGraphicsDevice - DefaultGraphicsConfiguration add 'setScreen(..)' - MutableGraphicsConfiguration - Make DefaultGraphicsConfiguration.setScreen(..) public - NativeWindowFactory add 'createScreen(String type, AbstractGraphicsDevice device, int screen)' - Refactored from SWTAccessor - NativeWindow x11ErrorHandler: Dump Stack Trace in DEBUG mode, always. --- .../com/jogamp/opengl/GLAutoDrawableDelegate.java | 8 +- .../com/jogamp/opengl/util/GLDrawableUtil.java | 116 +------ .../classes/javax/media/opengl/GLAutoDrawable.java | 32 +- src/jogl/classes/javax/media/opengl/GLContext.java | 23 +- .../classes/jogamp/opengl/GLAutoDrawableBase.java | 13 +- src/jogl/classes/jogamp/opengl/GLContextImpl.java | 24 +- .../classes/jogamp/opengl/GLDrawableHelper.java | 31 +- .../jogamp/opengl/GLEventListenerState.java | 267 ++++++++++++++++ .../nativewindow/MutableGraphicsConfiguration.java | 7 + .../jogamp/nativewindow/egl/EGLGraphicsDevice.java | 11 +- .../com/jogamp/nativewindow/swt/SWTAccessor.java | 19 +- .../jogamp/nativewindow/x11/X11GraphicsDevice.java | 12 +- .../AbstractGraphicsConfiguration.java | 3 +- .../media/nativewindow/AbstractGraphicsDevice.java | 12 +- .../media/nativewindow/AbstractGraphicsScreen.java | 2 + .../nativewindow/DefaultGraphicsConfiguration.java | 17 +- .../media/nativewindow/DefaultGraphicsDevice.java | 19 +- .../media/nativewindow/DefaultGraphicsScreen.java | 6 +- .../media/nativewindow/NativeWindowFactory.java | 27 ++ src/nativewindow/native/x11/Xmisc.c | 2 +- .../classes/com/jogamp/newt/opengl/GLWindow.java | 18 +- src/newt/classes/jogamp/newt/WindowImpl.java | 9 +- .../jogamp/newt/driver/x11/WindowDriver.java | 10 +- .../acore/TestGLContextDrawableSwitch01NEWT.java | 330 +++++++++++++++++++ .../acore/TestGLContextDrawableSwitch11NEWT.java | 352 +++++++++++++++++++++ .../acore/TestGLContextDrawableSwitchNEWT.java | 310 ------------------ .../test/junit/util/GLEventListenerCounter.java | 65 ++++ 27 files changed, 1212 insertions(+), 533 deletions(-) create mode 100644 src/jogl/classes/jogamp/opengl/GLEventListenerState.java create mode 100644 src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextDrawableSwitch01NEWT.java create mode 100644 src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextDrawableSwitch11NEWT.java delete mode 100644 src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextDrawableSwitchNEWT.java create mode 100644 src/test/com/jogamp/opengl/test/junit/util/GLEventListenerCounter.java (limited to 'src') diff --git a/src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java b/src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java index 38a8deef8..38315dc72 100644 --- a/src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java +++ b/src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java @@ -65,7 +65,10 @@ import jogamp.opengl.GLDrawableImpl; public class GLAutoDrawableDelegate extends GLAutoDrawableBase implements GLAutoDrawable { /** * @param drawable a valid and already realized {@link GLDrawable} - * @param context a valid {@link GLContext}, may not be made current (created) yet. + * @param context a valid {@link GLContext}, + * may not have been made current (created) yet, + * may not be associated w/ drawable yet, + * may be null for lazy initialization * @param upstreamWidget optional UI element holding this instance, see {@link #getUpstreamWidget()}. * @param ownDevice pass true if {@link AbstractGraphicsDevice#close()} shall be issued, * otherwise pass false. Closing the device is required in case @@ -78,9 +81,6 @@ public class GLAutoDrawableDelegate extends GLAutoDrawableBase implements GLAuto if(null == drawable) { throw new IllegalArgumentException("null drawable"); } - if(null == context) { - throw new IllegalArgumentException("null context"); - } if(!drawable.isRealized()) { throw new IllegalArgumentException("drawable not realized"); } diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java b/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java index cc81e4820..c03e4bfa4 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java +++ b/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java @@ -27,17 +27,14 @@ */ package com.jogamp.opengl.util; -import java.util.ArrayList; -import java.util.List; - import javax.media.opengl.GLAnimatorControl; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLContext; import javax.media.opengl.GLDrawable; import javax.media.opengl.GLEventListener; -import javax.media.opengl.GLRunnable; import jogamp.opengl.Debug; +import jogamp.opengl.GLEventListenerState; /** * Providing utility functions dealing w/ {@link GLDrawable}s, {@link GLAutoDrawable} and their {@link GLEventListener}. @@ -83,7 +80,7 @@ public class GLDrawableUtil { dest.addGLEventListener(listener); if(preserveInitState && initialized) { dest.setGLEventListenerInitState(listener, true); - dest.invoke(false, new ReshapeGLEventListener(listener)); + dest.invoke(false, new GLEventListenerState.ReshapeGLEventListener(listener)); } // else .. !init state is default } @@ -121,108 +118,13 @@ public class GLDrawableUtil { * @param b */ public static final void swapGLContextAndAllGLEventListener(GLAutoDrawable a, GLAutoDrawable b) { - final List aGLCmds = new ArrayList(); - final List bGLCmds = new ArrayList(); - final GLAnimatorControl aAnim = a.getAnimator(); - final GLAnimatorControl bAnim = b.getAnimator(); - final boolean aIsPaused = isAnimatorAnimatingOnOtherThread(aAnim) && aAnim.pause(); - final boolean bIsPaused = isAnimatorAnimatingOnOtherThread(bAnim) && bAnim.pause(); - - // - // remove and cache all GLEventListener and their init-state - // - final int aSz = a.getGLEventListenerCount(); - final GLEventListener[] aGLE = new GLEventListener[aSz]; - final boolean[] aInit = new boolean[aSz]; - for(int i=0; idrawable might be an inner GLDrawable instance if using a delegation pattern, - * or this GLAutoDrawable instance. + * Associate the new context, newtCtx, to this auto-drawable. *

- * If the old or new context was current on this thread, it is being released before switching the drawable. + * The current context will be dis-associated from this auto-drawable + * via {@link GLContext#setGLDrawable(GLDrawable, boolean) setGLDrawable(null, true);} first. + *

+ *

+ * The new context will be associated with this auto-drawable + * via {@link GLContext#setGLDrawable(GLDrawable, boolean) newCtx.setGLDrawable(drawable, true);}. + *

+ *

+ * If the old or new context was current on this thread, it is being released before switching the association. * The new context will be made current afterwards, if it was current before. * However the user shall take extra care that no other thread * attempts to make this context current. *

*

- * Be aware that the old context is still bound to the drawable, - * and that one context can only be bound to one drawable at one time! - *

- *

* In case you do not intend to use the old context anymore, i.e. - * not assigning it to another drawable, it shall be - * destroyed before setting the new context, i.e.: + * not assigning it to another drawable, it shall be + * destroyed, i.e.: *

-            GLContext oldCtx = glad.getContext();
+            GLContext oldCtx = glad.setContext(newCtx);
             if(null != oldCtx) {
                 oldCtx.destroy();
             }
-            glad.setContext(newCtx);            
    * 
- * This is required, since a context must have a valid drawable at all times - * and this API shall not restrict the user in any way. *

* - * @param newCtx the new context - * @return the replaced GLContext, maybe null + * @param newCtx the new context, maybe null for dis-association. + * @return the previous GLContext, maybe null * * @see GLContext#setGLDrawable(GLDrawable, boolean) * @see GLContext#setGLReadDrawable(GLDrawable) diff --git a/src/jogl/classes/javax/media/opengl/GLContext.java b/src/jogl/classes/javax/media/opengl/GLContext.java index 455f2d70d..4817add4d 100644 --- a/src/jogl/classes/javax/media/opengl/GLContext.java +++ b/src/jogl/classes/javax/media/opengl/GLContext.java @@ -217,15 +217,20 @@ public abstract class GLContext { /** * Sets the read/write drawable for framebuffer operations. *

+ * If the arguments reflect the current state of this context + * this method is a no-operation and returns the old and current {@link GLDrawable}. + *

+ *

* If the context was current on this thread, it is being released before switching the drawable * and made current afterwards. However the user shall take extra care that not other thread * attempts to make this context current. Otherwise a race condition may happen. *

- * @param readWrite the read/write drawable for framebuffer operations. - * @param setWriteOnly if true and if the current read-drawable differs - * from the write-drawable ({@link #setGLReadDrawable(GLDrawable)}), - * only change the write-drawable. Otherwise set both drawables. - * @return the replaced read/write drawable + * @param readWrite The read/write drawable for framebuffer operations, maybe null to remove association. + * @param setWriteOnly Only change the write-drawable, if setWriteOnly is true and + * if the {@link #getGLReadDrawable() read-drawable} differs + * from the {@link #getGLDrawable() write-drawable}. + * Otherwise set both drawables, read and write. + * @return The previous read/write drawable * * @throws GLException in case null is being passed or * this context is made current on another thread. @@ -239,6 +244,12 @@ public abstract class GLContext { /** * Returns the write-drawable this context uses for framebuffer operations. + *

+ * If the read-drawable has not been changed manually via {@link #setGLReadDrawable(GLDrawable)}, + * it equals to the write-drawable (default). + *

+ * @see #setGLDrawable(GLDrawable, boolean) + * @see #setGLReadDrawable(GLDrawable) */ public abstract GLDrawable getGLDrawable(); @@ -259,7 +270,7 @@ public abstract class GLContext { * * @param read the read-drawable for read framebuffer operations. * If null is passed, the default write drawable will be set. - * @return the replaced read-drawable + * @return the previous read-drawable * * @throws GLException in case a read drawable is not supported or * this context is made current on another thread. diff --git a/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java b/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java index eadd59559..c20197e72 100644 --- a/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java +++ b/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java @@ -74,8 +74,12 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, FPSCounter { protected volatile boolean sendDestroy = false; // volatile: maybe written by WindowManager thread w/o locking /** - * @param drawable upstream {@link GLDrawableImpl} instance, may be null for lazy initialization - * @param context upstream {@link GLContextImpl} instance, may be null for lazy initialization + * @param drawable upstream {@link GLDrawableImpl} instance, + * may be null for lazy initialization + * @param context upstream {@link GLContextImpl} instance, + * may not have been made current (created) yet, + * may not be associated w/ drawable yet, + * may be null for lazy initialization * @param ownsDevice pass true if {@link AbstractGraphicsDevice#close()} shall be issued, * otherwise pass false. Closing the device is required in case * the drawable is created w/ it's own new instance, e.g. offscreen drawables, @@ -85,6 +89,9 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, FPSCounter { this.drawable = drawable; this.context = context; this.ownsDevice = ownsDevice; + if(null != context && null != drawable) { + context.setGLDrawable(drawable, false); + } resetFPSCounter(); } @@ -326,7 +333,7 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, FPSCounter { final GLContext oldCtx = context; final boolean newCtxCurrent = GLDrawableHelper.switchContext(drawable, oldCtx, newCtx, additionalCtxCreationFlags); context=(GLContextImpl)newCtx; - if(newCtxCurrent) { + if(newCtxCurrent) { // implies null != newCtx context.makeCurrent(); } return oldCtx; diff --git a/src/jogl/classes/jogamp/opengl/GLContextImpl.java b/src/jogl/classes/jogamp/opengl/GLContextImpl.java index f2c2cfada..2a2b6a8fd 100644 --- a/src/jogl/classes/jogamp/opengl/GLContextImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLContextImpl.java @@ -203,8 +203,8 @@ public abstract class GLContextImpl extends GLContext { @Override public final GLDrawable setGLDrawable(GLDrawable readWrite, boolean setWriteOnly) { - if(null==readWrite) { - throw new GLException("Null read/write drawable not allowed"); + if( drawable == readWrite && ( setWriteOnly || drawableRead == readWrite ) ) { + return drawable; // no change. } final boolean lockHeld = lock.isOwner(Thread.currentThread()); if(lockHeld) { @@ -212,16 +212,20 @@ public abstract class GLContextImpl extends GLContext { } else if(lock.isLockedByOtherThread()) { // still could glitch .. throw new GLException("GLContext current by other thread ("+lock.getOwner()+"), operation not allowed."); } - if(!setWriteOnly || drawableRead==drawable) { // if !setWriteOnly || !explicitReadDrawable + if( !setWriteOnly || drawableRead == drawable ) { // if !setWriteOnly || !explicitReadDrawable drawableRead = (GLDrawableImpl) readWrite; } final GLDrawableImpl old = drawable; - old.associateContext(this, false); - drawableRetargeted = null != drawable; + if( null != old ) { + old.associateContext(this, false); + } + drawableRetargeted |= null != drawable && readWrite != drawable; drawable = (GLDrawableImpl) readWrite ; - drawable.associateContext(this, true); - if(lockHeld) { - makeCurrent(); + if( null != drawable ) { + drawable.associateContext(this, true); + if( lockHeld ) { + makeCurrent(); + } } return old; } @@ -334,7 +338,7 @@ public abstract class GLContextImpl extends GLContext { ", surf "+toHexString(drawable.getHandle())+", isShared "+GLContextShareSet.isShared(this)+" - "+lock); } if (contextHandle != 0) { - int lockRes = drawable.lockSurface(); + final int lockRes = drawable.lockSurface(); if (NativeSurface.LOCK_SURFACE_NOT_READY == lockRes) { // this would be odd .. throw new GLException("Surface not ready to lock: "+drawable); @@ -408,7 +412,7 @@ public abstract class GLContextImpl extends GLContext { throw new GLException("Destination OpenGL context has not been created"); } - int lockRes = drawable.lockSurface(); + final int lockRes = drawable.lockSurface(); if (NativeSurface.LOCK_SURFACE_NOT_READY == lockRes) { // this would be odd .. throw new GLException("Surface not ready to lock"); diff --git a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java index f8c58ee34..5d113ff83 100644 --- a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java +++ b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java @@ -131,30 +131,36 @@ public class GLDrawableHelper { } /** - * Associate a new context to the drawable and also propagates the context/drawable switch by - * calling {@link GLContext#setGLDrawable(GLDrawable, boolean) newCtx.setGLDrawable(drawable, true);}. - *

- * If the old or new context was current on this thread, it is being released before switching the drawable. + * Switch {@link GLContext} / {@link GLDrawable} association. + *

+ * Dis-associate oldCtx from drawable + * via {@link GLContext#setGLDrawable(GLDrawable, boolean) oldCtx.setGLDrawable(null, true);}. *

*

- * Be aware that the old context is still bound to the drawable, - * and that one context can only bound to one drawable at one time! + * Re-associate newCtx with drawable + * via {@link GLContext#setGLDrawable(GLDrawable, boolean) newCtx.setGLDrawable(drawable, true);}. + *

+ *

+ * If the old or new context was current on this thread, it is being released before switching the drawable. *

*

* No locking is being performed on the drawable, caller is required to take care of it. *

* * @param drawable the drawable which context is changed - * @param oldCtx the old context - * @param newCtx the new context + * @param oldCtx the old context, maybe null. + * @param newCtx the new context, maybe null 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) */ public static final boolean switchContext(GLDrawable drawable, GLContext oldCtx, GLContext newCtx, int newCtxCreationFlags) { - if( null != oldCtx && oldCtx.isCurrent() ) { - oldCtx.release(); + if( null != oldCtx ) { + if( oldCtx.isCurrent() ) { + oldCtx.release(); + } + oldCtx.setGLDrawable(null, true); // dis-associate old pair } final boolean newCtxCurrent; if(null!=newCtx) { @@ -163,8 +169,8 @@ public class GLDrawableHelper { newCtx.release(); } newCtx.setContextCreationFlags(newCtxCreationFlags); - newCtx.setGLDrawable(drawable, true); // propagate context/drawable switch - } else { + newCtx.setGLDrawable(drawable, true); // re-associate new pair + } else { newCtxCurrent = false; } return newCtxCurrent; @@ -203,6 +209,7 @@ public class GLDrawableHelper { } context.getGL().glFinish(); context.release(); + context.setGLDrawable(null, true); // dis-associate } if(null != proxySurface) { diff --git a/src/jogl/classes/jogamp/opengl/GLEventListenerState.java b/src/jogl/classes/jogamp/opengl/GLEventListenerState.java new file mode 100644 index 000000000..dea2bc85b --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/GLEventListenerState.java @@ -0,0 +1,267 @@ +/** + * 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 jogamp.opengl; + +import java.util.ArrayList; +import java.util.List; + +import javax.media.nativewindow.AbstractGraphicsConfiguration; +import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.AbstractGraphicsScreen; +import javax.media.nativewindow.NativeWindowFactory; +import javax.media.nativewindow.VisualIDHolder; +import javax.media.opengl.GLAnimatorControl; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLException; +import javax.media.opengl.GLRunnable; + +import com.jogamp.nativewindow.MutableGraphicsConfiguration; + +/** + * GLEventListenerState is holding {@link GLAutoDrawable} components crucial + * to relocating all its {@link GLEventListener} w/ their operating {@link GLContext}, etc. + * The components are: + *
    + *
  • {@link AbstractGraphicsScreen}
  • + *
  • {@link GLCapabilitiesImmutable}
  • + *
  • {@link GLContext} operating all {@link GLEventListener}
  • + *
  • All {@link GLEventListener}
  • + *
  • All {@link GLEventListener}'s init state
  • + *
  • {@link GLAnimatorControl}
  • + *
+ *

+ * A GLEventListenerState instance can be created while components are {@link #moveFrom(GLAutoDrawable) moved from} a {@link GLAutoDrawable} + * to the new instance, which gains {@link #isOwner() ownership} of the moved components. + *

+ *

+ * A GLEventListenerState instance's components can be {@link #moveTo(GLAutoDrawable) moved to} a {@link GLAutoDrawable}, + * while loosing {@link #isOwner() ownership} of the moved components. + *

+ *

+ */ +public class GLEventListenerState { + private GLEventListenerState(AbstractGraphicsScreen screen, GLCapabilitiesImmutable caps, GLContext context, int count, GLAnimatorControl anim) { + this.screen = screen; + this.caps = caps; + this.context = context; + this.listeners = new GLEventListener[count]; + this.listenersInit = new boolean[count]; + this.anim = anim; + this.owner = true; + } + /** + * Returns true, if this instance is the current owner of the components, + * otherwise false. + *

+ * Ownership is lost if {@link #moveTo(GLAutoDrawable)} is being called successfully + * and all components are transferred to the new {@link GLAutoDrawable}. + *

+ */ + public final boolean isOwner() { return owner; } + + public final int listenerCount() { return listeners.length; } + + public final AbstractGraphicsScreen screen; + public final GLCapabilitiesImmutable caps; + public final GLContext context; + public final GLEventListener[] listeners; + public final boolean[] listenersInit; + public final GLAnimatorControl anim; + + private boolean owner; + + /** + * Last resort to destroy and loose ownership + */ + public void destroy() { + if( owner ) { + final int aSz = listenerCount(); + for(int i=0; i + * Note that all components are removed from the {@link GLAutoDrawable}, + * i.e. the {@link GLContext}, all {@link GLEventListener}. + *

+ *

+ * If the {@link GLAutoDrawable} was added to a {@link GLAnimatorControl}, it is removed + * and the {@link GLAnimatorControl} added to the GLEventListenerState. + *

+ *

+ * The returned GLEventListenerState instance is the {@link #isOwner() owner of the components}. + *

+ * + * @param a {@link GLAutoDrawable} source to move components from + * @return new GLEventListenerState instance {@link #isOwner() owning} moved components. + * + * @see #moveTo(GLAutoDrawable) + */ + public static GLEventListenerState moveFrom(GLAutoDrawable a) { + final int aSz = a.getGLEventListenerCount(); + + // Create new AbstractGraphicsScreen w/ cloned AbstractGraphicsDevice for future GLAutoDrawable + // allowing this AbstractGraphicsDevice to loose ownership -> not closing display/device! + final AbstractGraphicsConfiguration aCfg1 = a.getNativeSurface().getGraphicsConfiguration(); + final GLCapabilitiesImmutable caps1 = (GLCapabilitiesImmutable) aCfg1.getChosenCapabilities(); + final AbstractGraphicsScreen aScreen1 = aCfg1.getScreen(); + final AbstractGraphicsDevice aDevice1 = aScreen1.getDevice(); + final AbstractGraphicsDevice aDevice2 = (AbstractGraphicsDevice) aDevice1.clone(); + final AbstractGraphicsScreen aScreen2 = NativeWindowFactory.createScreen( NativeWindowFactory.getNativeWindowType(false), aDevice2, aScreen1.getIndex() ); + + final GLAnimatorControl aAnim = a.getAnimator(); + if( null != aAnim ) { + aAnim.remove(a); // also handles ECT + } + + final GLEventListenerState glls = new GLEventListenerState(aScreen2, caps1, a.getContext(), aSz, aAnim); + + // + // remove and cache all GLEventListener and their init-state + // + for(int i=0; i + * If the previous {@link GLAutoDrawable} was removed from a {@link GLAnimatorControl} by previous {@link #moveFrom(GLAutoDrawable)}, + * the given {@link GLAutoDrawable} is added to the cached {@link GLAnimatorControl}. + * This operation is skipped, if the given {@link GLAutoDrawable} is already added to a {@link GLAnimatorControl} instance. + *

+ *

+ * Note: After this operation, the GLEventListenerState reference should be released. + *

+ * + * @param a {@link GLAutoDrawable} destination to move GLEventListenerState components to + * + * @throws GLException if the {@link GLAutoDrawable}'s configuration is incompatible, i.e. different {@link GLCapabilitiesImmutable}. + * + * @see #moveFrom(GLAutoDrawable) + * @see #isOwner() + */ + public final void moveTo(GLAutoDrawable a) { + final List aGLCmds = new ArrayList(); + final int aSz = listenerCount(); + + final MutableGraphicsConfiguration aCfg = (MutableGraphicsConfiguration) a.getNativeSurface().getGraphicsConfiguration(); + final GLCapabilitiesImmutable aCaps = (GLCapabilitiesImmutable) aCfg.getChosenCapabilities(); + if( caps.getVisualID(VisualIDHolder.VIDType.INTRINSIC) != aCaps.getVisualID(VisualIDHolder.VIDType.INTRINSIC) ) { + throw new GLException("XXX: Incompatible - Prev Holder: "+caps+", New Holder "+caps); + } + final GLContext prevContext = a.getContext(); + if( null != prevContext) { + prevContext.destroy(); + } + final AbstractGraphicsScreen preScreen = aCfg.getScreen(); + aCfg.setScreen( screen ); + preScreen.getDevice().close(); + a.setContext( context ); + owner = false; + + // + // Trigger GL-Viewport reset and reshape of all initialized GLEventListeners + // + aGLCmds.add(setViewport); + for(int i=0; i= 0 is specific screen * @return - * @throws UnsupportedOperationException */ - public static AbstractGraphicsScreen getScreen(AbstractGraphicsDevice device, int screen) throws UnsupportedOperationException { - if( isX11 ) { - X11GraphicsDevice x11Device = (X11GraphicsDevice)device; - if(0 > screen) { - screen = x11Device.getDefaultScreen(); - } - return new X11GraphicsScreen(x11Device, screen); - } - if(0 > screen) { - screen = 0; // FIXME: Needs native API utilization - } - if( isWindows || isOSX ) { - return new DefaultGraphicsScreen(device, screen); - } - throw new UnsupportedOperationException("n/a for this windowing system: "+nwt); + public static AbstractGraphicsScreen getScreen(AbstractGraphicsDevice device, int screen) { + return NativeWindowFactory.createScreen(nwt, device, screen); } public static int getNativeVisualID(AbstractGraphicsDevice device, long windowHandle) { diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/x11/X11GraphicsDevice.java b/src/nativewindow/classes/com/jogamp/nativewindow/x11/X11GraphicsDevice.java index 0f28ca67c..da3b31de4 100644 --- a/src/nativewindow/classes/com/jogamp/nativewindow/x11/X11GraphicsDevice.java +++ b/src/nativewindow/classes/com/jogamp/nativewindow/x11/X11GraphicsDevice.java @@ -45,7 +45,7 @@ import javax.media.nativewindow.ToolkitLock; */ public class X11GraphicsDevice extends DefaultGraphicsDevice implements Cloneable { - final boolean handleOwner; + /* final */ boolean handleOwner; final boolean isXineramaEnabled; /** Constructs a new X11GraphicsDevice corresponding to the given connection and default @@ -153,5 +153,13 @@ public class X11GraphicsDevice extends DefaultGraphicsDevice implements Cloneabl } return super.close(); } + + @Override + public boolean isHandleOwner() { + return handleOwner; + } + @Override + public void clearHandleOwner() { + handleOwner = false; + } } - diff --git a/src/nativewindow/classes/javax/media/nativewindow/AbstractGraphicsConfiguration.java b/src/nativewindow/classes/javax/media/nativewindow/AbstractGraphicsConfiguration.java index ebdaf2fbb..4e45113d4 100644 --- a/src/nativewindow/classes/javax/media/nativewindow/AbstractGraphicsConfiguration.java +++ b/src/nativewindow/classes/javax/media/nativewindow/AbstractGraphicsConfiguration.java @@ -42,8 +42,9 @@ package javax.media.nativewindow; /** A marker interface describing a graphics configuration, visual, or pixel format in a toolkit-independent manner. */ - public interface AbstractGraphicsConfiguration extends VisualIDHolder, Cloneable { + public Object clone(); + /** * Return the screen this graphics configuration is valid for */ diff --git a/src/nativewindow/classes/javax/media/nativewindow/AbstractGraphicsDevice.java b/src/nativewindow/classes/javax/media/nativewindow/AbstractGraphicsDevice.java index 8ecd5242d..585cd1f09 100644 --- a/src/nativewindow/classes/javax/media/nativewindow/AbstractGraphicsDevice.java +++ b/src/nativewindow/classes/javax/media/nativewindow/AbstractGraphicsDevice.java @@ -45,7 +45,6 @@ import jogamp.nativewindow.Debug; /** A interface describing a graphics device in a toolkit-independent manner. */ - public interface AbstractGraphicsDevice extends Cloneable { public static final boolean DEBUG = Debug.debug("GraphicsDevice"); @@ -58,6 +57,8 @@ public interface AbstractGraphicsDevice extends Cloneable { /** Default unit id for the 1st device: 0 */ public static int DEFAULT_UNIT = 0; + public Object clone(); + /** * Returns the type of the underlying subsystem, ie * NativeWindowFactory.TYPE_KD, NativeWindowFactory.TYPE_X11, .. @@ -143,10 +144,17 @@ public interface AbstractGraphicsDevice extends Cloneable { *

* Example implementations like {@link com.jogamp.nativewindow.x11.X11GraphicsDevice} * or {@link com.jogamp.nativewindow.egl.EGLGraphicsDevice} - * issue the native close operation or skip it depending on the handles's ownership. + * issue the native close operation or skip it depending on the {@link #isHandleOwner() handles's ownership}. *

* * @return true if the handle was not null and closing was successful, otherwise false. */ public boolean close(); + + /** + * @return true if instance owns the handle to issue {@link #close()}, otherwise false. + */ + public boolean isHandleOwner(); + + public void clearHandleOwner(); } diff --git a/src/nativewindow/classes/javax/media/nativewindow/AbstractGraphicsScreen.java b/src/nativewindow/classes/javax/media/nativewindow/AbstractGraphicsScreen.java index eb2cc9120..acb98073b 100644 --- a/src/nativewindow/classes/javax/media/nativewindow/AbstractGraphicsScreen.java +++ b/src/nativewindow/classes/javax/media/nativewindow/AbstractGraphicsScreen.java @@ -44,6 +44,8 @@ package javax.media.nativewindow; */ public interface AbstractGraphicsScreen extends Cloneable { + public Object clone(); + /** * Return the device this graphics configuration is valid for */ diff --git a/src/nativewindow/classes/javax/media/nativewindow/DefaultGraphicsConfiguration.java b/src/nativewindow/classes/javax/media/nativewindow/DefaultGraphicsConfiguration.java index a33c3792a..6b23172e1 100644 --- a/src/nativewindow/classes/javax/media/nativewindow/DefaultGraphicsConfiguration.java +++ b/src/nativewindow/classes/javax/media/nativewindow/DefaultGraphicsConfiguration.java @@ -93,25 +93,26 @@ public class DefaultGraphicsConfiguration implements Cloneable, AbstractGraphics /** * Set the capabilities to a new value. * + *

* The use case for setting the Capabilities at a later time is - * a change of the graphics device in a multi-screen environment.
- * + * a change or re-validation of capabilities. + *

* @see javax.media.nativewindow.GraphicsConfigurationFactory#chooseGraphicsConfiguration(Capabilities, CapabilitiesChooser, AbstractGraphicsScreen) */ protected void setChosenCapabilities(CapabilitiesImmutable capsChosen) { - capabilitiesChosen = capsChosen; + this.capabilitiesChosen = capsChosen; } /** * Set a new screen. * + *

* the use case for setting a new screen at a later time is - * a change of the graphics device in a multi-screen environment.
- * - * A copy of the passed object is being used. + * a change of the graphics device in a multi-screen environment. + *

*/ - final protected void setScreen(DefaultGraphicsScreen screen) { - this.screen = (AbstractGraphicsScreen) screen.clone(); + protected void setScreen(AbstractGraphicsScreen screen) { + this.screen = screen; } @Override diff --git a/src/nativewindow/classes/javax/media/nativewindow/DefaultGraphicsDevice.java b/src/nativewindow/classes/javax/media/nativewindow/DefaultGraphicsDevice.java index 9288652d9..b3ae4628c 100644 --- a/src/nativewindow/classes/javax/media/nativewindow/DefaultGraphicsDevice.java +++ b/src/nativewindow/classes/javax/media/nativewindow/DefaultGraphicsDevice.java @@ -37,10 +37,10 @@ import jogamp.nativewindow.NativeWindowFactoryImpl; public class DefaultGraphicsDevice implements Cloneable, AbstractGraphicsDevice { private static final String separator = "_"; - private String type; - protected String connection; - protected int unitID; - protected String uniqueID; + private final String type; + protected final String connection; + protected final int unitID; + protected final String uniqueID; protected long handle; protected ToolkitLock toolkitLock; @@ -170,9 +170,18 @@ public class DefaultGraphicsDevice implements Cloneable, AbstractGraphicsDevice return false; } + @Override + public boolean isHandleOwner() { + return false; + } + + @Override + public void clearHandleOwner() { + } + @Override public String toString() { - return getClass().getSimpleName()+"[type "+getType()+", connection "+getConnection()+", unitID "+getUnitID()+", handle 0x"+Long.toHexString(getHandle())+", "+toolkitLock+"]"; + return getClass().getSimpleName()+"[type "+getType()+", connection "+getConnection()+", unitID "+getUnitID()+", handle 0x"+Long.toHexString(getHandle())+", owner "+isHandleOwner()+", "+toolkitLock+"]"; } /** diff --git a/src/nativewindow/classes/javax/media/nativewindow/DefaultGraphicsScreen.java b/src/nativewindow/classes/javax/media/nativewindow/DefaultGraphicsScreen.java index f50bd0e14..9fa58c7a3 100644 --- a/src/nativewindow/classes/javax/media/nativewindow/DefaultGraphicsScreen.java +++ b/src/nativewindow/classes/javax/media/nativewindow/DefaultGraphicsScreen.java @@ -33,8 +33,8 @@ package javax.media.nativewindow; public class DefaultGraphicsScreen implements Cloneable, AbstractGraphicsScreen { - AbstractGraphicsDevice device; - private int idx; + private final AbstractGraphicsDevice device; + private final int idx; public DefaultGraphicsScreen(AbstractGraphicsDevice device, int idx) { this.device = device; @@ -57,7 +57,7 @@ public class DefaultGraphicsScreen implements Cloneable, AbstractGraphicsScreen public AbstractGraphicsDevice getDevice() { return device; } - + public int getIndex() { return idx; } diff --git a/src/nativewindow/classes/javax/media/nativewindow/NativeWindowFactory.java b/src/nativewindow/classes/javax/media/nativewindow/NativeWindowFactory.java index d7f28a986..07702c762 100644 --- a/src/nativewindow/classes/javax/media/nativewindow/NativeWindowFactory.java +++ b/src/nativewindow/classes/javax/media/nativewindow/NativeWindowFactory.java @@ -48,6 +48,10 @@ import jogamp.nativewindow.ResourceToolkitLock; import com.jogamp.common.os.Platform; import com.jogamp.common.util.ReflectionUtil; +import com.jogamp.nativewindow.awt.AWTGraphicsDevice; +import com.jogamp.nativewindow.awt.AWTGraphicsScreen; +import com.jogamp.nativewindow.x11.X11GraphicsDevice; +import com.jogamp.nativewindow.x11.X11GraphicsScreen; /** Provides a pluggable mechanism for arbitrary window toolkits to adapt their components to the {@link NativeWindow} interface, @@ -428,6 +432,29 @@ public abstract class NativeWindowFactory { return NativeWindowFactoryImpl.getNullToolkitLock(); } + /** + * @param device + * @param screen -1 is default screen of the given device, e.g. maybe 0 or determined by native API. >= 0 is specific screen + * @return newly created AbstractGraphicsScreen of given native type + */ + public static AbstractGraphicsScreen createScreen(String type, AbstractGraphicsDevice device, int screen) { + if( TYPE_X11 == type ) { + final X11GraphicsDevice x11Device = (X11GraphicsDevice)device; + if(0 > screen) { + screen = x11Device.getDefaultScreen(); + } + return new X11GraphicsScreen(x11Device, screen); + } + if(0 > screen) { + screen = 0; // FIXME: Needs native API utilization + } + if( TYPE_AWT == type ) { + final AWTGraphicsDevice awtDevice = (AWTGraphicsDevice) device; + return new AWTGraphicsScreen(awtDevice); + } + return new DefaultGraphicsScreen(device, screen); + } + /** Returns the appropriate NativeWindowFactory to handle window objects of the given type. The windowClass might be {@link NativeWindow NativeWindow}, in which case the client has diff --git a/src/nativewindow/native/x11/Xmisc.c b/src/nativewindow/native/x11/Xmisc.c index a8d45f288..017c52df2 100644 --- a/src/nativewindow/native/x11/Xmisc.c +++ b/src/nativewindow/native/x11/Xmisc.c @@ -180,7 +180,7 @@ static int errorHandlerThrowException = 0; static int x11ErrorHandler(Display *dpy, XErrorEvent *e) { - if(!errorHandlerQuiet) { + if( !errorHandlerQuiet || errorHandlerDebug ) { const char * errnoStr = strerror(errno); char errCodeStr[80]; char reqCodeStr[80]; diff --git a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java index 96d0f6e3b..7fccb6622 100644 --- a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java +++ b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java @@ -458,24 +458,22 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind } else { t0 = 0; } - - /* if (nativeWindowCreated && null != context) { - throw new GLException("InternalError: Native Windows has been just created, but context wasn't destroyed (is not null)"); - } */ - if (null == context && visible && 0 != window.getWindowHandle() && 0 ctx1/draw2, ctx2/draw1. + */ +public class TestGLContextDrawableSwitch01NEWT extends UITestCase { + static GLProfile glp; + static GLCapabilities caps; + static int width, height; + + @BeforeClass + public static void initClass() { + glp = GLProfile.getGL2ES2(); + caps = new GLCapabilities(glp); + width = 256; + height = 256; + } + + private GLAutoDrawable createGLAutoDrawable(GLCapabilities caps, int x, int y, int width, int height, WindowListener wl) throws InterruptedException { + final Window window = NewtFactory.createWindow(caps); + Assert.assertNotNull(window); + window.setPosition(x, y); + window.setSize(width, height); + window.setVisible(true); + Assert.assertTrue(AWTRobotUtil.waitForVisible(window, true)); + Assert.assertTrue(AWTRobotUtil.waitForRealized(window, true)); + + final GLDrawableFactory factory = GLDrawableFactory.getFactory(caps.getGLProfile()); + final GLDrawable drawable = factory.createGLDrawable(window); + Assert.assertNotNull(drawable); + + drawable.setRealized(true); + Assert.assertTrue(drawable.isRealized()); + + final GLContext context = drawable.createContext(null); + Assert.assertNotNull(context); + + final GLAutoDrawableDelegate glad = new GLAutoDrawableDelegate(drawable, context, window, false, null) { + @Override + protected void destroyImplInLock() { + super.destroyImplInLock(); + window.destroy(); // destroys the actual window + } + }; + + // add basic window interaction + window.addWindowListener(new WindowAdapter() { + @Override + public void windowRepaint(WindowUpdateEvent e) { + glad.windowRepaintOp(); + } + @Override + public void windowResized(WindowEvent e) { + glad.windowResizedOp(window.getWidth(), window.getHeight()); + } + @Override + public void windowDestroyNotify(WindowEvent e) { + glad.windowDestroyNotifyOp(); + } + }); + window.addWindowListener(wl); + + return glad; + } + + @Test(timeout=30000) + public void testSwitch2WindowSingleContext() throws InterruptedException { + final QuitAdapter quitAdapter = new QuitAdapter(); + + GLAutoDrawable glad1 = createGLAutoDrawable(caps, 64, 64, width, height, quitAdapter); + GLAutoDrawable glad2 = createGLAutoDrawable(caps, 2*64+width, 64, width+100, height+100, quitAdapter); + + // create single context using glad1 and assign it to glad1, + // destroy the prev. context afterwards. + { + final GLContext newCtx = glad1.createContext(null); + Assert.assertNotNull(newCtx); + final GLContext oldCtx = glad1.setContext(newCtx); + Assert.assertNotNull(oldCtx); + oldCtx.destroy(); + final int res = newCtx.makeCurrent(); + Assert.assertTrue(GLContext.CONTEXT_CURRENT_NEW==res || GLContext.CONTEXT_CURRENT==res); + newCtx.release(); + } + + final SnapshotGLEventListener snapshotGLEventListener = new SnapshotGLEventListener(); + GearsES2 gears = new GearsES2(1); + glad1.addGLEventListener(gears); + glad1.addGLEventListener(snapshotGLEventListener); + snapshotGLEventListener.setMakeSnapshot(); + + Animator animator = new Animator(); + animator.add(glad1); + animator.add(glad2); + animator.start(); + + int s = 0; + long t0 = System.currentTimeMillis(); + long t1 = t0; + + while( !quitAdapter.shouldQuit() && ( t1 - t0 ) < duration ) { + if( ( t1 - t0 ) / period > s) { + s++; + System.err.println(s+" - switch - START "+ ( t1 - t0 )); + + // switch context _and_ the demo synchronously + GLDrawableUtil.swapGLContextAndAllGLEventListener(glad1, glad2); + + System.err.println(s+" - switch - END "+ ( t1 - t0 )); + } + Thread.sleep(100); + t1 = System.currentTimeMillis(); + } + + animator.stop(); + glad1.destroy(); + glad2.destroy(); + } + + @Test(timeout=30000) + public void testSwitch2GLWindowOneDemo() throws InterruptedException { + final SnapshotGLEventListener snapshotGLEventListener = new SnapshotGLEventListener(); + final GearsES2 gears = new GearsES2(1); + final QuitAdapter quitAdapter = new QuitAdapter(); + + GLWindow glWindow1 = GLWindow.create(caps); + glWindow1.setTitle("win1"); + glWindow1.setSize(width, height); + glWindow1.setPosition(64, 64); + glWindow1.addGLEventListener(0, gears); + glWindow1.addGLEventListener(snapshotGLEventListener); + glWindow1.addWindowListener(quitAdapter); + + GLWindow glWindow2 = GLWindow.create(caps); + glWindow2.setTitle("win2"); + glWindow2.setSize(width+100, height+100); + glWindow2.setPosition(2*64+width, 64); + glWindow2.addWindowListener(quitAdapter); + + Animator animator = new Animator(); + animator.add(glWindow1); + animator.add(glWindow2); + animator.start(); + + glWindow1.setVisible(true); + glWindow2.setVisible(true); + + snapshotGLEventListener.setMakeSnapshot(); + + int s = 0; + long t0 = System.currentTimeMillis(); + long t1 = t0; + + while( !quitAdapter.shouldQuit() && ( t1 - t0 ) < duration ) { + if( ( t1 - t0 ) / period > s) { + s++; + System.err.println(s+" - switch - START "+ ( t1 - t0 )); + System.err.println(s+" - A w1-h 0x"+Long.toHexString(glWindow1.getHandle())+",-ctx 0x"+Long.toHexString(glWindow1.getContext().getHandle())); + System.err.println(s+" - A w2-h 0x"+Long.toHexString(glWindow2.getHandle())+",-ctx 0x"+Long.toHexString(glWindow2.getContext().getHandle())); + + // switch context _and_ the demo synchronously + GLDrawableUtil.swapGLContextAndAllGLEventListener(glWindow1, glWindow2); + + System.err.println(s+" - B w1-h 0x"+Long.toHexString(glWindow1.getHandle())+",-ctx 0x"+Long.toHexString(glWindow1.getContext().getHandle())); + System.err.println(s+" - B w2-h 0x"+Long.toHexString(glWindow2.getHandle())+",-ctx 0x"+Long.toHexString(glWindow2.getContext().getHandle())); + System.err.println(s+" - switch - END "+ ( t1 - t0 )); + + snapshotGLEventListener.setMakeSnapshot(); + } + Thread.sleep(100); + t1 = System.currentTimeMillis(); + } + + animator.stop(); + glWindow1.destroy(); + glWindow2.destroy(); + + } + + @Test(timeout=30000) + public void testSwitch2GLWindowEachWithOwnDemo() throws InterruptedException { + final GearsES2 gears = new GearsES2(1); + final RedSquareES2 rsquare = new RedSquareES2(1); + final QuitAdapter quitAdapter = new QuitAdapter(); + final SnapshotGLEventListener snapshotGLEventListener1 = new SnapshotGLEventListener(); + final SnapshotGLEventListener snapshotGLEventListener2 = new SnapshotGLEventListener(); + + GLWindow glWindow1 = GLWindow.create(caps); + glWindow1.setTitle("win1"); + glWindow1.setSize(width, height); + glWindow1.setPosition(64, 64); + glWindow1.addGLEventListener(0, gears); + glWindow1.addGLEventListener(snapshotGLEventListener1); + glWindow1.addWindowListener(quitAdapter); + + GLWindow glWindow2 = GLWindow.create(caps); + glWindow2.setTitle("win2"); + glWindow2.setSize(width+100, height+100); + glWindow2.setPosition(2*64+width, 64); + glWindow2.addGLEventListener(0, rsquare); + glWindow2.addGLEventListener(snapshotGLEventListener2); + glWindow2.addWindowListener(quitAdapter); + + Animator animator = new Animator(); + animator.add(glWindow1); + animator.add(glWindow2); + animator.start(); + + glWindow1.setVisible(true); + glWindow2.setVisible(true); + + snapshotGLEventListener1.setMakeSnapshot(); + snapshotGLEventListener2.setMakeSnapshot(); + + int s = 0; + long t0 = System.currentTimeMillis(); + long t1 = t0; + + while( !quitAdapter.shouldQuit() && ( t1 - t0 ) < duration ) { + if( ( t1 - t0 ) / period > s) { + s++; + System.err.println(s+" - switch - START "+ ( t1 - t0 )); + System.err.println(s+" - A w1-h 0x"+Long.toHexString(glWindow1.getHandle())+",-ctx 0x"+Long.toHexString(glWindow1.getContext().getHandle())); + System.err.println(s+" - A w2-h 0x"+Long.toHexString(glWindow2.getHandle())+",-ctx 0x"+Long.toHexString(glWindow2.getContext().getHandle())); + GLDrawableUtil.swapGLContextAndAllGLEventListener(glWindow1, glWindow2); + System.err.println(s+" - B w1-h 0x"+Long.toHexString(glWindow1.getHandle())+",-ctx 0x"+Long.toHexString(glWindow1.getContext().getHandle())); + System.err.println(s+" - B w2-h 0x"+Long.toHexString(glWindow2.getHandle())+",-ctx 0x"+Long.toHexString(glWindow2.getContext().getHandle())); + System.err.println(s+" - switch - END "+ ( t1 - t0 )); + snapshotGLEventListener1.setMakeSnapshot(); + snapshotGLEventListener2.setMakeSnapshot(); + } + Thread.sleep(100); + t1 = System.currentTimeMillis(); + } + + animator.stop(); + // System.err.println("pre -del-w1: w1: "+glWindow1); + // System.err.println("pre -del-w1: w2: "+glWindow2); + glWindow1.destroy(); + // System.err.println("post-del-w1: w1: "+glWindow1); + // System.err.println("post-del-w1: w2: "+glWindow2); + glWindow2.destroy(); + + } + + // default timing for 2 switches + static long duration = 2200; // ms + static long period = 1000; // ms + + public static void main(String args[]) throws IOException { + for(int i=0; i + * See Bug 665 - https://jogamp.org/bugzilla/show_bug.cgi?id=665. + *

+ */ +public class TestGLContextDrawableSwitch11NEWT extends UITestCase { + static GLProfile glp; + static GLCapabilities caps; + static int width, height; + + @BeforeClass + public static void initClass() { + glp = GLProfile.getGL2ES2(); + caps = new GLCapabilities(glp); + width = 256; + height = 256; + } + + private GLAutoDrawable createGLAutoDrawable(GLCapabilities caps, int x, int y, int width, int height, WindowListener wl) throws InterruptedException { + final Window window = NewtFactory.createWindow(caps); + Assert.assertNotNull(window); + window.setPosition(x, y); + window.setSize(width, height); + window.setVisible(true); + Assert.assertTrue(AWTRobotUtil.waitForVisible(window, true)); + Assert.assertTrue(AWTRobotUtil.waitForRealized(window, true)); + + final GLDrawableFactory factory = GLDrawableFactory.getFactory(caps.getGLProfile()); + final GLDrawable drawable = factory.createGLDrawable(window); + Assert.assertNotNull(drawable); + + drawable.setRealized(true); + Assert.assertTrue(drawable.isRealized()); + + final GLAutoDrawableDelegate glad = new GLAutoDrawableDelegate(drawable, null, window, false, null) { + @Override + protected void destroyImplInLock() { + super.destroyImplInLock(); + window.destroy(); // destroys the actual window + } + }; + + // add basic window interaction + window.addWindowListener(new WindowAdapter() { + @Override + public void windowRepaint(WindowUpdateEvent e) { + glad.windowRepaintOp(); + } + @Override + public void windowResized(WindowEvent e) { + glad.windowResizedOp(window.getWidth(), window.getHeight()); + } + @Override + public void windowDestroyNotify(WindowEvent e) { + glad.windowDestroyNotifyOp(); + } + }); + window.addWindowListener(wl); + + return glad; + } + + @Test(timeout=30000) + public void test01() throws InterruptedException { + final QuitAdapter quitAdapter = new QuitAdapter(); + + 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 GLAutoDrawable glad1 = createGLAutoDrawable(caps, 64, 64, width, height, quitAdapter); + final GLContext context1 = glad1.createContext(null); + glad1.setContext(context1); + animator.add(glad1); + + glad1.addGLEventListener(glelCounter); + glad1.addGLEventListener(new GearsES2(1)); + glad1.addGLEventListener(snapshotGLEventListener); + snapshotGLEventListener.setMakeSnapshot(); + + 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, glelCounter.initCount); + Assert.assertTrue(1 <= glelCounter.reshapeCount); + Assert.assertTrue(1 <= glelCounter.displayCount); + Assert.assertEquals(0, glelCounter.disposeCount); + Assert.assertEquals(context1, glad1.getContext()); + Assert.assertEquals(3, glad1.getGLEventListenerCount()); + Assert.assertEquals(context1.getGLReadDrawable(), glad1.getDelegatedDrawable()); + Assert.assertEquals(context1.getGLDrawable(), glad1.getDelegatedDrawable()); + + glls1 = GLEventListenerState.moveFrom(glad1); + + Assert.assertEquals(1, glelCounter.initCount); + Assert.assertTrue(1 <= glelCounter.reshapeCount); + Assert.assertTrue(1 <= glelCounter.displayCount); + Assert.assertEquals(0, glelCounter.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, glelCounter.initCount); + Assert.assertTrue(1 <= glelCounter.reshapeCount); + Assert.assertTrue(1 <= glelCounter.displayCount); + Assert.assertEquals(0, glelCounter.disposeCount); + } + + // - create glad2 w/ survived context + { + final GLAutoDrawable glad2 = createGLAutoDrawable(caps, 2*64+width, 64, width+100, height+100, quitAdapter); + snapshotGLEventListener.setMakeSnapshot(); + + Assert.assertEquals(null, glad2.getContext()); + Assert.assertEquals(0, glad2.getGLEventListenerCount()); + + glls1.moveTo(glad2); + + Assert.assertEquals(1, glelCounter.initCount); + Assert.assertTrue(1 <= glelCounter.reshapeCount); + Assert.assertTrue(1 <= glelCounter.displayCount); + Assert.assertEquals(0, glelCounter.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, glelCounter.initCount); + Assert.assertTrue(1 <= glelCounter.reshapeCount); + Assert.assertTrue(1 <= glelCounter.displayCount); + Assert.assertEquals(1, glelCounter.disposeCount); + } + animator.stop(); + } + + @Test(timeout=30000) + public void test02() 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); + glad1.setVisible(true); + animator.add(glad1); + + glad1.addGLEventListener(glelTracker); + glad1.addGLEventListener(new GearsES2(1)); + glad1.addGLEventListener(snapshotGLEventListener); + snapshotGLEventListener.setMakeSnapshot(); + + 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); + glad2.setVisible(true); + snapshotGLEventListener.setMakeSnapshot(); + + 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 s) { - s++; - System.err.println(s+" - switch - START "+ ( t1 - t0 )); - - // switch context _and_ the demo synchronously - GLDrawableUtil.swapGLContextAndAllGLEventListener(glad1, glad2); - - System.err.println(s+" - switch - END "+ ( t1 - t0 )); - } - Thread.sleep(100); - t1 = System.currentTimeMillis(); - } - - animator.stop(); - glad1.destroy(); - glad2.destroy(); - } - - @Test(timeout=30000) - public void testSwitch2GLWindowOneDemo() throws InterruptedException { - GearsES2 gears = new GearsES2(1); - final QuitAdapter quitAdapter = new QuitAdapter(); - - GLWindow glWindow1 = GLWindow.create(caps); - glWindow1.setTitle("win1"); - glWindow1.setSize(width, height); - glWindow1.setPosition(64, 64); - glWindow1.addGLEventListener(0, gears); - glWindow1.addWindowListener(quitAdapter); - - GLWindow glWindow2 = GLWindow.create(caps); - glWindow2.setTitle("win2"); - glWindow2.setSize(width+100, height+100); - glWindow2.setPosition(2*64+width, 64); - glWindow2.addWindowListener(quitAdapter); - - Animator animator = new Animator(); - animator.add(glWindow1); - animator.add(glWindow2); - animator.start(); - - glWindow1.setVisible(true); - glWindow2.setVisible(true); - - int s = 0; - long t0 = System.currentTimeMillis(); - long t1 = t0; - - while( !quitAdapter.shouldQuit() && ( t1 - t0 ) < duration ) { - if( ( t1 - t0 ) / period > s) { - s++; - System.err.println(s+" - switch - START "+ ( t1 - t0 )); - System.err.println(s+" - A w1-h 0x"+Long.toHexString(glWindow1.getHandle())+",-ctx 0x"+Long.toHexString(glWindow1.getContext().getHandle())); - System.err.println(s+" - A w2-h 0x"+Long.toHexString(glWindow2.getHandle())+",-ctx 0x"+Long.toHexString(glWindow2.getContext().getHandle())); - - // switch context _and_ the demo synchronously - GLDrawableUtil.swapGLContextAndAllGLEventListener(glWindow1, glWindow2); - - System.err.println(s+" - B w1-h 0x"+Long.toHexString(glWindow1.getHandle())+",-ctx 0x"+Long.toHexString(glWindow1.getContext().getHandle())); - System.err.println(s+" - B w2-h 0x"+Long.toHexString(glWindow2.getHandle())+",-ctx 0x"+Long.toHexString(glWindow2.getContext().getHandle())); - System.err.println(s+" - switch - END "+ ( t1 - t0 )); - } - Thread.sleep(100); - t1 = System.currentTimeMillis(); - } - - animator.stop(); - glWindow1.destroy(); - glWindow2.destroy(); - - } - - @Test(timeout=30000) - public void testSwitch2GLWindowEachWithOwnDemo() throws InterruptedException { - GearsES2 gears = new GearsES2(1); - RedSquareES2 rsquare = new RedSquareES2(1); - final QuitAdapter quitAdapter = new QuitAdapter(); - - GLWindow glWindow1 = GLWindow.create(caps); - glWindow1.setTitle("win1"); - glWindow1.setSize(width, height); - glWindow1.setPosition(64, 64); - glWindow1.addGLEventListener(0, gears); - glWindow1.addWindowListener(quitAdapter); - - GLWindow glWindow2 = GLWindow.create(caps); - glWindow2.setTitle("win2"); - glWindow2.setSize(width+100, height+100); - glWindow2.setPosition(2*64+width, 64); - glWindow2.addGLEventListener(0, rsquare); - glWindow2.addWindowListener(quitAdapter); - - Animator animator = new Animator(); - animator.add(glWindow1); - animator.add(glWindow2); - animator.start(); - - glWindow1.setVisible(true); - glWindow2.setVisible(true); - - int s = 0; - long t0 = System.currentTimeMillis(); - long t1 = t0; - - while( !quitAdapter.shouldQuit() && ( t1 - t0 ) < duration ) { - if( ( t1 - t0 ) / period > s) { - s++; - System.err.println(s+" - switch - START "+ ( t1 - t0 )); - System.err.println(s+" - A w1-h 0x"+Long.toHexString(glWindow1.getHandle())+",-ctx 0x"+Long.toHexString(glWindow1.getContext().getHandle())); - System.err.println(s+" - A w2-h 0x"+Long.toHexString(glWindow2.getHandle())+",-ctx 0x"+Long.toHexString(glWindow2.getContext().getHandle())); - GLDrawableUtil.swapGLContextAndAllGLEventListener(glWindow1, glWindow2); - System.err.println(s+" - B w1-h 0x"+Long.toHexString(glWindow1.getHandle())+",-ctx 0x"+Long.toHexString(glWindow1.getContext().getHandle())); - System.err.println(s+" - B w2-h 0x"+Long.toHexString(glWindow2.getHandle())+",-ctx 0x"+Long.toHexString(glWindow2.getContext().getHandle())); - System.err.println(s+" - switch - END "+ ( t1 - t0 )); - } - Thread.sleep(100); - t1 = System.currentTimeMillis(); - } - - animator.stop(); - // System.err.println("pre -del-w1: w1: "+glWindow1); - // System.err.println("pre -del-w1: w2: "+glWindow2); - glWindow1.destroy(); - // System.err.println("post-del-w1: w1: "+glWindow1); - // System.err.println("post-del-w1: w2: "+glWindow2); - glWindow2.destroy(); - - } - - // default timing for 2 switches - static long duration = 2200; // ms - static long period = 1000; // ms - - public static void main(String args[]) throws IOException { - for(int i=0; i