diff options
author | Sven Gothel <[email protected]> | 2013-01-24 17:24:22 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2013-01-24 17:24:22 +0100 |
commit | b738983638703bb721ee4c9820c8ef43e2252e73 (patch) | |
tree | 5543e1be50c497619b6902676e7f66fdfaea78cd /src/jogl | |
parent | 85d70b7d38885fa8ba6374aa790d5a296acc8ec1 (diff) |
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.
Diffstat (limited to 'src/jogl')
8 files changed, 355 insertions, 159 deletions
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/ <code>drawable<code> yet, + * may be <code>null</code> for lazy initialization * @param upstreamWidget optional UI element holding this instance, see {@link #getUpstreamWidget()}. * @param ownDevice pass <code>true</code> if {@link AbstractGraphicsDevice#close()} shall be issued, * otherwise pass <code>false</code>. 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<GLRunnable> aGLCmds = new ArrayList<GLRunnable>(); - final List<GLRunnable> bGLCmds = new ArrayList<GLRunnable>(); - 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; i<aSz; i++) { - final GLEventListener l = a.getGLEventListener(0); - aInit[i] = a.getGLEventListenerInitState(l); - aGLE[i] = a.removeGLEventListener( l ); - } - final int bSz = b.getGLEventListenerCount(); - final GLEventListener[] bGLE = new GLEventListener[bSz]; - final boolean[] bInit = new boolean[bSz]; - for(int i=0; i<bSz; i++) { - final GLEventListener l = b.getGLEventListener(0); - bInit[i] = b.getGLEventListenerInitState(l); - bGLE[i] = b.removeGLEventListener( l ); - } - - // - // trigger glFinish to sync GL ctx - // - a.invoke(true, glFinish); - b.invoke(true, glFinish); - - // - // switch context and - // trigger GL-Viewport reset and reshape of all initialized GLEventListeners - // - b.setContext( a.setContext( b.getContext() ) ); - aGLCmds.add(setViewport); - bGLCmds.add(setViewport); - for(int i=0; i<aSz; i++) { - if( aInit[i] ) { - bGLCmds.add(new ReshapeGLEventListener(aGLE[i])); - } - } - for(int i=0; i<bSz; i++) { - if( bInit[i] ) { - aGLCmds.add(new ReshapeGLEventListener(bGLE[i])); - } - } - aGLCmds.add(glFinish); - bGLCmds.add(glFinish); - a.invoke(true, aGLCmds); - b.invoke(true, bGLCmds); - - // add all cached GLEventListener to their destination and fix their init-state - for(int i=0; i<bSz; i++) { - final GLEventListener l = bGLE[i]; - a.addGLEventListener( l ); - if( bInit[i] ) { - a.setGLEventListenerInitState(l, true); - } // else uninitialized is default after add - } - for(int i=0; i<aSz; i++) { - final GLEventListener l = aGLE[i]; - b.addGLEventListener( l ); - if( aInit[i] ) { - b.setGLEventListenerInitState(l, true); - } // else uninitialized is default after add - } + final GLEventListenerState gllsA = GLEventListenerState.moveFrom(a); + final GLEventListenerState gllsB = GLEventListenerState.moveFrom(b); - if(aIsPaused) { aAnim.resume(); } - if(bIsPaused) { bAnim.resume(); } + gllsA.moveTo(b); + gllsB.moveTo(a); } - static GLRunnable setViewport = new GLRunnable() { - @Override - public boolean run(GLAutoDrawable drawable) { - drawable.getGL().glViewport(0, 0, drawable.getWidth(), drawable.getHeight()); - return true; - } - }; - static GLRunnable glFinish = new GLRunnable() { - @Override - public boolean run(GLAutoDrawable drawable) { - drawable.getGL().glFinish(); - return true; - } - }; - - private static class ReshapeGLEventListener implements GLRunnable { - private GLEventListener listener; - ReshapeGLEventListener(GLEventListener listener) { - this.listener = listener; - } - @Override - public boolean run(GLAutoDrawable drawable) { - listener.reshape(drawable, 0, 0, drawable.getWidth(), drawable.getHeight()); - return true; - } - } - /** * Swaps the {@link GLContext} of given {@link GLAutoDrawable} * and {@link GLAutoDrawable#disposeGLEventListener(GLEventListener, boolean) disposes} @@ -251,11 +153,11 @@ public class GLDrawableUtil { } dest.setContext( src.setContext( dest.getContext() ) ); - src.invoke(true, setViewport); - dest.invoke(true, setViewport); + src.invoke(true, GLEventListenerState.setViewport); + dest.invoke(true, GLEventListenerState.setViewport); if(aIsPaused) { aAnim.resume(); } if(bIsPaused) { bAnim.resume(); } } - + } diff --git a/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java b/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java index a7db3f3fd..612ff6bdb 100644 --- a/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java +++ b/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java @@ -130,37 +130,35 @@ public interface GLAutoDrawable extends GLDrawable { public GLContext getContext(); /** - * Associate a new context to this drawable and also propagates the context/drawable switch by - * calling {@link GLContext#setGLDrawable(GLDrawable, boolean) newCtx.setGLDrawable(drawable, true);}. - * <code>drawable</code> might be an inner GLDrawable instance if using a delegation pattern, - * or this GLAutoDrawable instance. + * Associate the new context, <code>newtCtx</code>, to this auto-drawable. * <p> - * 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. + * </p> + * <p> + * The new context will be associated with this auto-drawable + * via {@link GLContext#setGLDrawable(GLDrawable, boolean) newCtx.setGLDrawable(drawable, true);}. + * </p> + * <p> + * 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. * </p> * <p> - * 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! - * </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 before setting the new context, i.e.: + * not assigning it to another drawable, it shall be + * destroyed, i.e.: * <pre> - GLContext oldCtx = glad.getContext(); + GLContext oldCtx = glad.setContext(newCtx); if(null != oldCtx) { oldCtx.destroy(); } - glad.setContext(newCtx); * </pre> - * 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. * </p> * - * @param newCtx the new context - * @return the replaced GLContext, maybe <code>null</code> + * @param newCtx the new context, maybe <code>null</code> for dis-association. + * @return the previous GLContext, maybe <code>null</code> * * @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. * <p> + * If the arguments reflect the current state of this context + * this method is a no-operation and returns the old and current {@link GLDrawable}. + * </p> + * <p> * 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. * </p> - * @param readWrite the read/write drawable for framebuffer operations. - * @param setWriteOnly if <code>true</code> 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 <code>null</code> to remove association. + * @param setWriteOnly Only change the write-drawable, if <code>setWriteOnly</code> is <code>true</code> 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 <code>null</code> 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. + * <p> + * If the read-drawable has not been changed manually via {@link #setGLReadDrawable(GLDrawable)}, + * it equals to the write-drawable (default). + * </p> + * @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 <code>null</code> for lazy initialization + * @param context upstream {@link GLContextImpl} instance, + * may not have been made current (created) yet, + * may not be associated w/ <code>drawable<code> yet, + * may be <code>null</code> for lazy initialization * @param ownsDevice pass <code>true</code> if {@link AbstractGraphicsDevice#close()} shall be issued, * otherwise pass <code>false</code>. 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);}. - * <p> - * 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. + * <p> + * Dis-associate <code>oldCtx</code> from <code>drawable</code> + * via {@link GLContext#setGLDrawable(GLDrawable, boolean) oldCtx.setGLDrawable(null, true);}. * </p> * <p> - * 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 <code>newCtx</code> with <code>drawable</code> + * via {@link GLContext#setGLDrawable(GLDrawable, boolean) newCtx.setGLDrawable(drawable, true);}. + * </p> + * <p> + * If the old or new context was current on this thread, it is being released before switching the drawable. * </p> * <p> * No locking is being performed on the drawable, caller is required to take care of it. * </p> * * @param drawable the drawable which context is changed - * @param oldCtx the old context - * @param newCtx the new context + * @param oldCtx the old context, maybe <code>null</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) */ 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: + * <ul> + * <li>{@link AbstractGraphicsScreen}</li> + * <li>{@link GLCapabilitiesImmutable}</li> + * <li>{@link GLContext} operating all {@link GLEventListener}</li> + * <li>All {@link GLEventListener}</li> + * <li>All {@link GLEventListener}'s init state</li> + * <li>{@link GLAnimatorControl}</li> + * </ul> + * <p> + * 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. + * </p> + * <p> + * A GLEventListenerState instance's components can be {@link #moveTo(GLAutoDrawable) moved to} a {@link GLAutoDrawable}, + * while loosing {@link #isOwner() ownership} of the moved components. + * </p> + * <p> + */ +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 <code>true</code>, if this instance is the current owner of the components, + * otherwise <code>false</code>. + * <p> + * Ownership is lost if {@link #moveTo(GLAutoDrawable)} is being called successfully + * and all components are transferred to the new {@link GLAutoDrawable}. + * </p> + */ + 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<aSz; i++) { + listeners[i] = null; + } + // context.destroy(); - NPE (null drawable) + screen.getDevice().close(); + owner = false; + } + } + + /** + * Moves all GLEventListenerState components from the given {@link GLAutoDrawable} + * to a newly created instance. + * <p> + * Note that all components are removed from the {@link GLAutoDrawable}, + * i.e. the {@link GLContext}, all {@link GLEventListener}. + * </p> + * <p> + * If the {@link GLAutoDrawable} was added to a {@link GLAnimatorControl}, it is removed + * and the {@link GLAnimatorControl} added to the GLEventListenerState. + * </p> + * <p> + * The returned GLEventListenerState instance is the {@link #isOwner() owner of the components}. + * </p> + * + * @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<aSz; i++) { + final GLEventListener l = a.getGLEventListener(0); + glls.listenersInit[i] = a.getGLEventListenerInitState(l); + glls.listeners[i] = a.removeGLEventListener( l ); + } + + // + // trigger glFinish to sync GL ctx + // + a.invoke(true, glFinish); + + a.setContext( null ); + aDevice1.clearHandleOwner(); // don't close handle + + return glls; + } + + /** + * Moves all GLEventListenerState components to the given {@link GLAutoDrawable} + * from this instance, while loosing {@link #isOwner() ownership}. + * <p> + * 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. + * </p> + * <p> + * Note: After this operation, the GLEventListenerState reference should be released. + * </p> + * + * @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<GLRunnable> aGLCmds = new ArrayList<GLRunnable>(); + 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<aSz; i++) { + if( listenersInit[i] ) { + aGLCmds.add(new ReshapeGLEventListener( listeners[i] ) ); + } + } + aGLCmds.add(glFinish); + a.invoke(true, aGLCmds); + + // add all cached GLEventListener to their destination and fix their init-state + for(int i=0; i<aSz; i++) { + final GLEventListener l = listeners[i]; + a.addGLEventListener( l ); + if( listenersInit[i] ) { + a.setGLEventListenerInitState(l, true); + } // else uninitialized is default after add + listeners[i] = null; + } + + if( null != anim && null == a.getAnimator() ) { + anim.add(a); // also handles ECT + } + } + + public static GLRunnable setViewport = new GLRunnable() { + @Override + public boolean run(GLAutoDrawable drawable) { + drawable.getGL().glViewport(0, 0, drawable.getWidth(), drawable.getHeight()); + return true; + } + }; + + public static GLRunnable glFinish = new GLRunnable() { + @Override + public boolean run(GLAutoDrawable drawable) { + drawable.getGL().glFinish(); + return true; + } + }; + + public static class ReshapeGLEventListener implements GLRunnable { + private GLEventListener listener; + public ReshapeGLEventListener(GLEventListener listener) { + this.listener = listener; + } + @Override + public boolean run(GLAutoDrawable drawable) { + listener.reshape(drawable, 0, 0, drawable.getWidth(), drawable.getHeight()); + return true; + } + } +} |