summaryrefslogtreecommitdiffstats
path: root/src/jogl/classes/com/jogamp
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2014-07-27 03:49:21 +0200
committerSven Gothel <[email protected]>2014-07-27 03:49:21 +0200
commitc77b8f586cb2553582a42f5b90aeee5ef85f1efe (patch)
tree2f304461ff3d87b75f347dd5cf36a580aa73c854 /src/jogl/classes/com/jogamp
parent37760af388303834e359703aad9562ce6165845f (diff)
Bug 1033: Guarantee atomicity of high-level GLAutoDrawable operations, avoiding race conditions.
GLAutoDrawable (API CHANGE) allowing atomic operations: - Add class API-doc chapter about 'GLAutoDrawable Locking' - Add method invoke(..) API-doc description about throwing IllegalStateException in case of a detected deadlock situation ahead (Note: Implemented in GLDrawableHelper.invoke(..) for all implementations) - Add new methods for proper multithread handling: - public RecursiveLock getUpstreamLock(); - public boolean isThreadGLCapable(); +++ GLEventListenerState/GLDrawableUtil: - Perform operation in a atomic fashion, i.e. lock GLAutoDrawable during whole operations: - GLDrawableUtil.swapGLContext(..) - GLDrawableUtil.swapGLContextAndAllGLEventListener(..) - GLEventListenerState.moveFrom(..) - GLEventListenerState.moveTo(..) - ReshapeGLEventListener: - Moved from GLEventListenerState.ReshapeGLEventListener -> GLDrawableUtil.ReshapeGLEventListener - Takes 'displayAfterReshape' case into account. +++ javax.media.opengl.Threading Clarifications: - Public 'enum Mode', i.e. Threading.Mode - Public getMode() - Clarified 'isOpenGLThread()': - Take 'singleThreaded' into account directly, i.e. always return 'true' if singleThreaded == false
Diffstat (limited to 'src/jogl/classes/com/jogamp')
-rw-r--r--src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java2
-rw-r--r--src/jogl/classes/com/jogamp/opengl/GLEventListenerState.java491
-rw-r--r--src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java16
-rw-r--r--src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java372
4 files changed, 550 insertions, 331 deletions
diff --git a/src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java b/src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java
index f9a7ab029..4ef717a38 100644
--- a/src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java
+++ b/src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java
@@ -143,7 +143,7 @@ public class GLAutoDrawableDelegate extends GLAutoDrawableBase implements GLAuto
private final RecursiveLock lock;
@Override
- protected final RecursiveLock getLock() { return lock; }
+ public final RecursiveLock getUpstreamLock() { return lock; }
@Override
public final Object getUpstreamWidget() {
diff --git a/src/jogl/classes/com/jogamp/opengl/GLEventListenerState.java b/src/jogl/classes/com/jogamp/opengl/GLEventListenerState.java
index a7ba141bb..bfd5fe115 100644
--- a/src/jogl/classes/com/jogamp/opengl/GLEventListenerState.java
+++ b/src/jogl/classes/com/jogamp/opengl/GLEventListenerState.java
@@ -45,7 +45,9 @@ import javax.media.opengl.GLRunnable;
import jogamp.opengl.Debug;
+import com.jogamp.common.util.locks.RecursiveLock;
import com.jogamp.nativewindow.MutableGraphicsConfiguration;
+import com.jogamp.opengl.util.GLDrawableUtil;
/**
* GLEventListenerState is holding {@link GLAutoDrawable} components crucial
@@ -72,11 +74,14 @@ public class GLEventListenerState {
private GLEventListenerState(final AbstractGraphicsDevice upstreamDevice, final boolean proxyOwnsUpstreamDevice, final AbstractGraphicsDevice device,
final GLCapabilitiesImmutable caps,
+ final RecursiveLock upstreamLock, final NativeSurface lockedSurface,
final GLContext context, final int count, final GLAnimatorControl anim, final boolean animStarted) {
this.upstreamDevice = upstreamDevice;
this.proxyOwnsUpstreamDevice = proxyOwnsUpstreamDevice;
this.device = device;
this.caps = caps;
+ this.upstreamLock = upstreamLock;
+ this.lockedSurface = lockedSurface;
this.context = context;
this.listeners = new GLEventListener[count];
this.listenersInit = new boolean[count];
@@ -107,7 +112,29 @@ public class GLEventListenerState {
public final GLAnimatorControl anim;
public final boolean animStarted;
- private boolean owner;
+ private volatile RecursiveLock upstreamLock;
+ private volatile NativeSurface lockedSurface;
+ private volatile boolean owner;
+
+ /**
+ * Returns a {@link Runnable} {@link NativeSurface#unlockSurface() unlocking} an eventually locked {@link NativeSurface},
+ * see {@link #moveFrom(GLAutoDrawable, boolean)} and {@link #moveTo(GLAutoDrawable, Runnable)}.
+ */
+ public Runnable getUnlockSurfaceOp() { return unlockOp; }
+
+ private final Runnable unlockOp = new Runnable() {
+ public void run() {
+ final RecursiveLock rl = upstreamLock;
+ final NativeSurface ls = lockedSurface;
+ upstreamLock = null;
+ lockedSurface = null;
+ if( null != rl ) {
+ rl.unlock();
+ }
+ if( null != ls ) {
+ ls.unlockSurface();
+ }
+ } };
/**
* Last resort to destroy and loose ownership
@@ -119,6 +146,7 @@ public class GLEventListenerState {
listeners[i] = null;
}
// context.destroy(); - NPE (null drawable)
+ unlockOp.run();
device.close();
owner = false;
}
@@ -142,92 +170,143 @@ public class GLEventListenerState {
* <p>
* The returned GLEventListenerState instance is the {@link #isOwner() owner of the components}.
* </p>
+ * <p>
+ * Locking is performed on the {@link GLAutoDrawable auto-drawable's}
+ * {@link GLAutoDrawable#getUpstreamLock() upstream-lock} and {@link GLAutoDrawable#getNativeSurface() surface}.
+ * See <a href="../../../javax/media/opengl/GLAutoDrawable.html#locking">GLAutoDrawable Locking</a>.</li>
+ * </p>
*
- * @param a {@link GLAutoDrawable} source to move components from
+ * @param src {@link GLAutoDrawable} source to move components from
* @return new GLEventListenerState instance {@link #isOwner() owning} moved components.
*
* @see #moveTo(GLAutoDrawable)
*/
- public static GLEventListenerState moveFrom(final GLAutoDrawable a) {
- final GLAnimatorControl aAnim = a.getAnimator();
- final boolean aAnimStarted;
- if( null != aAnim ) {
- aAnimStarted = aAnim.isStarted();
- aAnim.remove(a); // also handles ECT
+ public static GLEventListenerState moveFrom(final GLAutoDrawable src) {
+ return GLEventListenerState.moveFrom(src, 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>
+ * <p>
+ * Locking is performed on the {@link GLAutoDrawable auto-drawable's}
+ * {@link GLAutoDrawable#getUpstreamLock() upstream-lock} and {@link GLAutoDrawable#getNativeSurface() surface},
+ * which is <i>not released</i> if <code>keepLocked</code> is <code>true</code>.
+ * See <a href="../../../javax/media/opengl/GLAutoDrawable.html#locking">GLAutoDrawable Locking</a>.</li>
+ * </p>
+ * <p>
+ * <code>keepLocked</code> may be utilized if swapping a context between drawables
+ * and to ensure atomicity of operation.
+ * Here, the {@link #getUnlockSurfaceOp()} shall be passed to {@link #moveTo(GLAutoDrawable, Runnable)}.
+ * See {@link GLDrawableUtil#swapGLContextAndAllGLEventListener(GLAutoDrawable, GLAutoDrawable)}.
+ * </p>
+ *
+ * @param src {@link GLAutoDrawable} source to move components from
+ * @param keepLocked keep {@link GLAutoDrawable#getUpstreamLock() upstream-lock} and {@link GLAutoDrawable#getNativeSurface() surface} locked, see above
+ * @return new GLEventListenerState instance {@link #isOwner() owning} moved components.
+ *
+ * @see #moveTo(GLAutoDrawable, Runnable)
+ */
+ public static GLEventListenerState moveFrom(final GLAutoDrawable src, final boolean keepLocked) {
+ final GLAnimatorControl srcAnim = src.getAnimator();
+ final boolean srcAnimStarted;
+ if( null != srcAnim ) {
+ srcAnimStarted = srcAnim.isStarted();
+ srcAnim.remove(src); // also handles ECT
} else {
- aAnimStarted = false;
+ srcAnimStarted = false;
}
final GLEventListenerState glls;
- final NativeSurface aSurface = a.getNativeSurface();
- final boolean surfaceLocked = false; // NativeSurface.LOCK_SURFACE_NOT_READY < aSurface.lockSurface();
+ final RecursiveLock srcUpstreamLock = src.getUpstreamLock();
+ srcUpstreamLock.lock();
try {
- 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 aCfg = aSurface.getGraphicsConfiguration();
- final GLCapabilitiesImmutable caps = (GLCapabilitiesImmutable) aCfg.getChosenCapabilities();
- final AbstractGraphicsDevice aDevice1 = aCfg.getScreen().getDevice();
- final AbstractGraphicsDevice aDevice2 = cloneDevice(aDevice1);
- aDevice1.clearHandleOwner(); // don't close device handle
- if( DEBUG ) {
- System.err.println("GLEventListenerState.moveFrom.0a: orig 0x"+Integer.toHexString(aDevice1.hashCode())+", "+aDevice1);
- System.err.println("GLEventListenerState.moveFrom.0b: pres 0x"+Integer.toHexString(aDevice2.hashCode())+", "+aDevice2);
- System.err.println("GLEventListenerState.moveFrom.1: "+aSurface.getClass().getName()/*+", "+aSurface*/);
+ final NativeSurface srcSurface = src.getNativeSurface();
+ final boolean srcSurfaceLocked = NativeSurface.LOCK_SURFACE_NOT_READY < srcSurface.lockSurface();
+ if( src.isRealized() && !srcSurfaceLocked ) {
+ throw new GLException("Could not lock realized surface "+src);
}
- final AbstractGraphicsDevice aUpDevice2;
- final boolean proxyOwnsUpstreamDevice;
- {
- AbstractGraphicsDevice _aUpDevice2 = null;
- if(aSurface instanceof ProxySurface) {
- final ProxySurface aProxy = (ProxySurface)aSurface;
- proxyOwnsUpstreamDevice = aProxy.containsUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE );
- final NativeSurface aUpSurface = aProxy.getUpstreamSurface();
- if(DEBUG && null != aUpSurface) {
- System.err.println("GLEventListenerState.moveFrom.2: "+aUpSurface.getClass().getName()+", "+aUpSurface);
- }
- if(null != aUpSurface) {
- final AbstractGraphicsDevice aUpDevice1 = aUpSurface.getGraphicsConfiguration().getScreen().getDevice();
- _aUpDevice2 = cloneDevice(aUpDevice1);
- aUpDevice1.clearHandleOwner(); // don't close device handle
- if(DEBUG) {
- System.err.println("GLEventListenerState.moveFrom.3a: up-orig 0x"+Integer.toHexString(aUpDevice1.hashCode())+", "+aUpDevice1);
- System.err.println("GLEventListenerState.moveFrom.3b: up-pres 0x"+Integer.toHexString(_aUpDevice2.hashCode())+", "+_aUpDevice2);
- System.err.println("GLEventListenerState.moveFrom.3c: "+aSurface.getClass().getName()+", "+aSurface);
- System.err.println("GLEventListenerState.moveFrom.3d: "+aUpSurface.getClass().getName()/*+", "+aUpSurface+", "*/+aProxy.getUpstreamOptionBits(null).toString());
+
+ try {
+ final int aSz = src.getGLEventListenerCount();
+
+ // Create new AbstractGraphicsScreen w/ cloned AbstractGraphicsDevice for future GLAutoDrawable
+ // allowing this AbstractGraphicsDevice to loose ownership -> not closing display/device!
+ final AbstractGraphicsConfiguration aCfg = srcSurface.getGraphicsConfiguration();
+ final GLCapabilitiesImmutable caps = (GLCapabilitiesImmutable) aCfg.getChosenCapabilities();
+ final AbstractGraphicsDevice aDevice1 = aCfg.getScreen().getDevice();
+ final AbstractGraphicsDevice aDevice2 = cloneDevice(aDevice1);
+ aDevice1.clearHandleOwner(); // don't close device handle
+ if( DEBUG ) {
+ System.err.println("GLEventListenerState.moveFrom.0a: orig 0x"+Integer.toHexString(aDevice1.hashCode())+", "+aDevice1);
+ System.err.println("GLEventListenerState.moveFrom.0b: pres 0x"+Integer.toHexString(aDevice2.hashCode())+", "+aDevice2);
+ System.err.println("GLEventListenerState.moveFrom.1: "+srcSurface.getClass().getName()/*+", "+aSurface*/);
+ }
+ final AbstractGraphicsDevice aUpDevice2;
+ final boolean proxyOwnsUpstreamDevice;
+ {
+ AbstractGraphicsDevice _aUpDevice2 = null;
+ if(srcSurface instanceof ProxySurface) {
+ final ProxySurface aProxy = (ProxySurface)srcSurface;
+ proxyOwnsUpstreamDevice = aProxy.containsUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE );
+ final NativeSurface aUpSurface = aProxy.getUpstreamSurface();
+ if(DEBUG && null != aUpSurface) {
+ System.err.println("GLEventListenerState.moveFrom.2: "+aUpSurface.getClass().getName()+", "+aUpSurface);
+ }
+ if(null != aUpSurface) {
+ final AbstractGraphicsDevice aUpDevice1 = aUpSurface.getGraphicsConfiguration().getScreen().getDevice();
+ _aUpDevice2 = cloneDevice(aUpDevice1);
+ aUpDevice1.clearHandleOwner(); // don't close device handle
+ if(DEBUG) {
+ System.err.println("GLEventListenerState.moveFrom.3a: up-orig 0x"+Integer.toHexString(aUpDevice1.hashCode())+", "+aUpDevice1);
+ System.err.println("GLEventListenerState.moveFrom.3b: up-pres 0x"+Integer.toHexString(_aUpDevice2.hashCode())+", "+_aUpDevice2);
+ System.err.println("GLEventListenerState.moveFrom.3c: "+srcSurface.getClass().getName()+", "+srcSurface);
+ System.err.println("GLEventListenerState.moveFrom.3d: "+aUpSurface.getClass().getName()/*+", "+aUpSurface+", "*/+aProxy.getUpstreamOptionBits(null).toString());
+ }
}
+ } else {
+ proxyOwnsUpstreamDevice = false;
}
- } else {
- proxyOwnsUpstreamDevice = false;
+ aUpDevice2 = _aUpDevice2;
}
- aUpDevice2 = _aUpDevice2;
- }
- glls = new GLEventListenerState(aUpDevice2, proxyOwnsUpstreamDevice, aDevice2, caps, a.getContext(), aSz, aAnim, aAnimStarted);
-
- //
- // 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);
+ glls = new GLEventListenerState(aUpDevice2, proxyOwnsUpstreamDevice, aDevice2, caps,
+ keepLocked ? srcUpstreamLock : null,
+ srcSurfaceLocked && keepLocked ? srcSurface : null,
+ src.getContext(), aSz, srcAnim, srcAnimStarted);
+
+ //
+ // remove and cache all GLEventListener and their init-state
+ //
+ for(int i=0; i<aSz; i++) {
+ final GLEventListener l = src.getGLEventListener(0);
+ glls.listenersInit[i] = src.getGLEventListenerInitState(l);
+ glls.listeners[i] = src.removeGLEventListener( l );
+ }
- a.setContext( null, false );
+ src.setContext( null, false ); // implicit glFinish() ctx/drawable sync
+ } finally {
+ if( srcSurfaceLocked && !keepLocked ) {
+ srcSurface.unlockSurface();
+ }
+ }
} finally {
- if( surfaceLocked ) {
- aSurface.unlockSurface();
+ if( !keepLocked ) {
+ srcUpstreamLock.unlock();
}
}
-
return glls;
}
@@ -240,134 +319,184 @@ public class GLEventListenerState {
* This operation is skipped, if the given {@link GLAutoDrawable} is already added to a {@link GLAnimatorControl} instance.
* </p>
* <p>
+ * Locking is performed on the {@link GLAutoDrawable auto-drawable's}
+ * {@link GLAutoDrawable#getUpstreamLock() upstream-lock} and {@link GLAutoDrawable#getNativeSurface() surface}.
+ * See <a href="../../../javax/media/opengl/GLAutoDrawable.html#locking">GLAutoDrawable Locking</a>.</li>
+ * </p>
+ * <p>
* Note: After this operation, the GLEventListenerState reference should be released.
* </p>
*
- * @param a {@link GLAutoDrawable} destination to move GLEventListenerState components to
+ * @param dest {@link GLAutoDrawable} destination to move GLEventListenerState components to
*
- * <!-- @throws GLException if the {@link GLAutoDrawable}'s configuration is incompatible, i.e. different {@link GLCapabilitiesImmutable}. -->
+ * @throws GLException if a realized surface could not be locked.
* @throws GLException if this preserved {@link AbstractGraphicsDevice} is incompatible w/ the given destination one.
+ * <!-- @throws GLException if the {@link GLAutoDrawable}'s configuration is incompatible, i.e. different {@link GLCapabilitiesImmutable}. -->
*
* @see #moveFrom(GLAutoDrawable)
* @see #isOwner()
*/
- public final void moveTo(final GLAutoDrawable a) {
- final GLAnimatorControl aAnim = a.getAnimator();
- final boolean hasAnimator = null != aAnim;
- final boolean aPaused;
- if( hasAnimator ) {
- aPaused = aAnim.pause();
- aAnim.remove(a); // also handles ECT
- if( aPaused ) {
- aAnim.resume();
- }
+ public final void moveTo(final GLAutoDrawable dest) throws GLException {
+ this.moveTo(dest, null);
+ }
+
+ /**
+ * 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, boolean)},
+ * 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>
+ * Locking is performed on the {@link GLAutoDrawable auto-drawable's}
+ * {@link GLAutoDrawable#getUpstreamLock() upstream-lock} and {@link GLAutoDrawable#getNativeSurface() surface}.
+ * See <a href="../../../javax/media/opengl/GLAutoDrawable.html#locking">GLAutoDrawable Locking</a>.</li>
+ * </p>
+ * <p>
+ * If the {@link GLAutoDrawable} <code>dest</code> has been kept locked by {@link #moveFrom(GLAutoDrawable, boolean)},
+ * it's {@link #getUnlockSurfaceOp()} shall be passed here to <code>destUnlockOperation</code> to be unlocked.
+ * </p>
+ * <p>
+ * Note: After this operation, the GLEventListenerState reference should be released.
+ * </p>
+ *
+ * @param dest {@link GLAutoDrawable} destination to move GLEventListenerState components to
+ * @param destUnlockOperation optional unlock operation for <code>dest</code>, see {@link #moveFrom(GLAutoDrawable, boolean)}.
+ *
+ * @throws GLException if a realized surface could not be locked.
+ * @throws GLException if this preserved {@link AbstractGraphicsDevice} is incompatible w/ the given destination one.
+ * <!-- @throws GLException if the {@link GLAutoDrawable}'s configuration is incompatible, i.e. different {@link GLCapabilitiesImmutable}. -->
+ *
+ * @see #moveFrom(GLAutoDrawable, boolean)
+ * @see #isOwner()
+ */
+ public final void moveTo(final GLAutoDrawable dest, final Runnable destUnlockOperation) throws GLException {
+ final GLAnimatorControl destAnim = dest.getAnimator();
+ final boolean destAnimPaused;
+ if( null != destAnim ) {
+ destAnimPaused = destAnim.pause();
+ destAnim.remove(dest); // also handles ECT
} else {
- aPaused = false;
+ destAnimPaused = false;
}
final List<GLRunnable> aGLCmds = new ArrayList<GLRunnable>();
final int aSz = listenerCount();
- final NativeSurface aSurface = a.getNativeSurface();
- final boolean surfaceLocked = false; // NativeSurface.LOCK_SURFACE_NOT_READY < aSurface.lockSurface();
- final boolean aRealized;
+ final RecursiveLock destUpstreamLock = dest.getUpstreamLock();
+ destUpstreamLock.lock();
+ final boolean destIsRealized;
try {
-
- final MutableGraphicsConfiguration aCfg = (MutableGraphicsConfiguration) aSurface.getGraphicsConfiguration();
- /**
- final GLCapabilitiesImmutable aCaps = (GLCapabilitiesImmutable) aCfg.getChosenCapabilities();
- if( caps.getVisualID(VisualIDHolder.VIDType.INTRINSIC) != aCaps.getVisualID(VisualIDHolder.VIDType.INTRINSIC) ||
- caps.getVisualID(VisualIDHolder.VIDType.NATIVE) != aCaps.getVisualID(VisualIDHolder.VIDType.NATIVE) ) {
- throw new GLException("Incompatible Capabilities - Prev-Holder: "+caps+", New-Holder "+caps);
- } */
- final DefaultGraphicsDevice aDevice1 = (DefaultGraphicsDevice) aCfg.getScreen().getDevice();
- final DefaultGraphicsDevice aDevice2 = (DefaultGraphicsDevice) device;
- if( !aDevice1.getUniqueID().equals( aDevice2.getUniqueID() ) ) {
- throw new GLException("Incompatible devices: Preserved <"+aDevice2.getUniqueID()+">, target <"+aDevice1.getUniqueID()+">");
- }
-
- // collect optional upstream surface info
- final ProxySurface aProxy;
- final NativeSurface aUpSurface;
- if(aSurface instanceof ProxySurface) {
- aProxy = (ProxySurface)aSurface;
- aUpSurface = aProxy.getUpstreamSurface();
- } else {
- aProxy = null;
- aUpSurface = null;
- }
- if( DEBUG ) {
- System.err.println("GLEventListenerState.moveTo.0 : has aProxy "+(null!=aProxy));
- System.err.println("GLEventListenerState.moveTo.0 : has aUpSurface "+(null!=aUpSurface));
- }
- if( null==aUpSurface && null != upstreamDevice ) {
- throw new GLException("Incompatible Surface config - Has Upstream-Surface: Prev-Holder = true, New-Holder = false");
- }
-
- // Destroy and remove currently associated GLContext, if any (will be replaced)
- a.setContext( null, true );
- aRealized = a.isRealized();
- if( aRealized && null != aUpSurface ) {
- // Unrealize due to device dependencies of an upstream surface, e.g. EGLUpstreamSurfaceHook
- a.getDelegatedDrawable().setRealized(false);
+ final NativeSurface destSurface = dest.getNativeSurface();
+ final boolean destSurfaceLocked = NativeSurface.LOCK_SURFACE_NOT_READY < destSurface.lockSurface();
+ if( dest.isRealized() && !destSurfaceLocked ) {
+ throw new GLException("Could not lock realized surface "+dest);
}
+ try {
+
+ final MutableGraphicsConfiguration aCfg = (MutableGraphicsConfiguration) destSurface.getGraphicsConfiguration();
+ /**
+ final GLCapabilitiesImmutable aCaps = (GLCapabilitiesImmutable) aCfg.getChosenCapabilities();
+ if( caps.getVisualID(VisualIDHolder.VIDType.INTRINSIC) != aCaps.getVisualID(VisualIDHolder.VIDType.INTRINSIC) ||
+ caps.getVisualID(VisualIDHolder.VIDType.NATIVE) != aCaps.getVisualID(VisualIDHolder.VIDType.NATIVE) ) {
+ throw new GLException("Incompatible Capabilities - Prev-Holder: "+caps+", New-Holder "+caps);
+ } */
+ final DefaultGraphicsDevice aDevice1 = (DefaultGraphicsDevice) aCfg.getScreen().getDevice();
+ final DefaultGraphicsDevice aDevice2 = (DefaultGraphicsDevice) device;
+ if( !aDevice1.getUniqueID().equals( aDevice2.getUniqueID() ) ) {
+ throw new GLException("Incompatible devices: Preserved <"+aDevice2.getUniqueID()+">, target <"+aDevice1.getUniqueID()+">");
+ }
- // Set new Screen and close previous one
- {
- if( DEBUG ) {
- System.err.println("GLEventListenerState.moveTo.0a: orig 0x"+Integer.toHexString(aDevice1.hashCode())+", "+aDevice1);
- System.err.println("GLEventListenerState.moveTo.0b: pres 0x"+Integer.toHexString(aDevice2.hashCode())+", "+aDevice2);
+ // collect optional upstream surface info
+ final ProxySurface aProxy;
+ final NativeSurface aUpSurface;
+ if(destSurface instanceof ProxySurface) {
+ aProxy = (ProxySurface)destSurface;
+ aUpSurface = aProxy.getUpstreamSurface();
+ } else {
+ aProxy = null;
+ aUpSurface = null;
}
- DefaultGraphicsDevice.swapDeviceHandleAndOwnership(aDevice1, aDevice2);
- aDevice2.close();
if( DEBUG ) {
- System.err.println("GLEventListenerState.moveTo.1a: orig 0x"+Integer.toHexString(aDevice1.hashCode())+", "+aDevice1);
- System.err.println("GLEventListenerState.moveTo.1b: pres 0x"+Integer.toHexString(aDevice2.hashCode())+", "+aDevice2);
+ System.err.println("GLEventListenerState.moveTo.0 : has aProxy "+(null!=aProxy));
+ System.err.println("GLEventListenerState.moveTo.0 : has aUpSurface "+(null!=aUpSurface));
+ }
+ if( null==aUpSurface && null != upstreamDevice ) {
+ throw new GLException("Incompatible Surface config - Has Upstream-Surface: Prev-Holder = true, New-Holder = false");
}
- }
- // If using a ProxySurface w/ an upstream surface, set new Screen and close previous one on it
- if( null != aUpSurface ) {
- final MutableGraphicsConfiguration aUpCfg = (MutableGraphicsConfiguration) aUpSurface.getGraphicsConfiguration();
- if( null != upstreamDevice ) {
- final DefaultGraphicsDevice aUpDevice1 = (DefaultGraphicsDevice) aUpCfg.getScreen().getDevice();
- final DefaultGraphicsDevice aUpDevice2 = (DefaultGraphicsDevice)upstreamDevice;
- if( !aUpDevice1.getUniqueID().equals( aUpDevice2.getUniqueID() ) ) {
- throw new GLException("Incompatible updtream devices: Preserved <"+aUpDevice2.getUniqueID()+">, target <"+aUpDevice1.getUniqueID()+">");
- }
+ // Destroy and remove currently associated GLContext, if any (will be replaced)
+ dest.setContext( null, true );
+ destIsRealized = dest.isRealized();
+ if( destIsRealized && null != aUpSurface ) {
+ // Unrealize due to device dependencies of an upstream surface, e.g. EGLUpstreamSurfaceHook
+ dest.getDelegatedDrawable().setRealized(false);
+ }
+
+ // Set new Screen and close previous one
+ {
if( DEBUG ) {
- System.err.println("GLEventListenerState.moveTo.2a: up-orig 0x"+Integer.toHexString(aUpDevice1.hashCode())+", "+aUpDevice1);
- System.err.println("GLEventListenerState.moveTo.2b: up-pres 0x"+Integer.toHexString(aUpDevice2.hashCode())+", "+aUpDevice2);
- System.err.println("GLEventListenerState.moveTo.2c: "+aUpSurface.getClass().getName()/*+", "+aUpSurface+", "*/+aProxy.getUpstreamOptionBits(null).toString());
- }
- DefaultGraphicsDevice.swapDeviceHandleAndOwnership(aUpDevice1, aUpDevice2);
- aUpDevice2.close();
- if( proxyOwnsUpstreamDevice ) {
- aProxy.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE );
+ System.err.println("GLEventListenerState.moveTo.0a: orig 0x"+Integer.toHexString(aDevice1.hashCode())+", "+aDevice1);
+ System.err.println("GLEventListenerState.moveTo.0b: pres 0x"+Integer.toHexString(aDevice2.hashCode())+", "+aDevice2);
}
+ DefaultGraphicsDevice.swapDeviceHandleAndOwnership(aDevice1, aDevice2);
+ aDevice2.close();
if( DEBUG ) {
- System.err.println("GLEventListenerState.moveTo.3a: up-orig 0x"+Integer.toHexString(aUpDevice1.hashCode())+", "+aUpDevice1);
- System.err.println("GLEventListenerState.moveTo.3b: up-pres 0x"+Integer.toHexString(aUpDevice2.hashCode())+", "+aUpDevice2);
- System.err.println("GLEventListenerState.moveTo.3c: "+aUpSurface.getClass().getName()/*+", "+aUpSurface+", "*/+aProxy.getUpstreamOptionBits(null).toString());
+ System.err.println("GLEventListenerState.moveTo.1a: orig 0x"+Integer.toHexString(aDevice1.hashCode())+", "+aDevice1);
+ System.err.println("GLEventListenerState.moveTo.1b: pres 0x"+Integer.toHexString(aDevice2.hashCode())+", "+aDevice2);
}
- } else {
- throw new GLException("Incompatible Surface config - Has Upstream-Surface: Prev-Holder = false, New-Holder = true");
}
- }
- if( aRealized && null != aUpSurface ) {
- a.getDelegatedDrawable().setRealized(true);
- }
- if( DEBUG ) {
- System.err.println("GLEventListenerState.moveTo.X : has aProxy "+(null!=aProxy));
- System.err.println("GLEventListenerState.moveTo.X : has aUpSurface "+(null!=aUpSurface));
+ // If using a ProxySurface w/ an upstream surface, set new Screen and close previous one on it
+ if( null != aUpSurface ) {
+ final MutableGraphicsConfiguration aUpCfg = (MutableGraphicsConfiguration) aUpSurface.getGraphicsConfiguration();
+ if( null != upstreamDevice ) {
+ final DefaultGraphicsDevice aUpDevice1 = (DefaultGraphicsDevice) aUpCfg.getScreen().getDevice();
+ final DefaultGraphicsDevice aUpDevice2 = (DefaultGraphicsDevice)upstreamDevice;
+ if( !aUpDevice1.getUniqueID().equals( aUpDevice2.getUniqueID() ) ) {
+ throw new GLException("Incompatible updtream devices: Preserved <"+aUpDevice2.getUniqueID()+">, target <"+aUpDevice1.getUniqueID()+">");
+ }
+ if( DEBUG ) {
+ System.err.println("GLEventListenerState.moveTo.2a: up-orig 0x"+Integer.toHexString(aUpDevice1.hashCode())+", "+aUpDevice1);
+ System.err.println("GLEventListenerState.moveTo.2b: up-pres 0x"+Integer.toHexString(aUpDevice2.hashCode())+", "+aUpDevice2);
+ System.err.println("GLEventListenerState.moveTo.2c: "+aUpSurface.getClass().getName()/*+", "+aUpSurface+", "*/+aProxy.getUpstreamOptionBits(null).toString());
+ }
+ DefaultGraphicsDevice.swapDeviceHandleAndOwnership(aUpDevice1, aUpDevice2);
+ aUpDevice2.close();
+ if( proxyOwnsUpstreamDevice ) {
+ aProxy.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE );
+ }
+ if( DEBUG ) {
+ System.err.println("GLEventListenerState.moveTo.3a: up-orig 0x"+Integer.toHexString(aUpDevice1.hashCode())+", "+aUpDevice1);
+ System.err.println("GLEventListenerState.moveTo.3b: up-pres 0x"+Integer.toHexString(aUpDevice2.hashCode())+", "+aUpDevice2);
+ System.err.println("GLEventListenerState.moveTo.3c: "+aUpSurface.getClass().getName()/*+", "+aUpSurface+", "*/+aProxy.getUpstreamOptionBits(null).toString());
+ }
+ } else {
+ throw new GLException("Incompatible Surface config - Has Upstream-Surface: Prev-Holder = false, New-Holder = true");
+ }
+ }
+
+ if( destIsRealized && null != aUpSurface ) {
+ dest.getDelegatedDrawable().setRealized(true);
+ }
+ if( DEBUG ) {
+ System.err.println("GLEventListenerState.moveTo.X : has aProxy "+(null!=aProxy));
+ System.err.println("GLEventListenerState.moveTo.X : has aUpSurface "+(null!=aUpSurface));
+ }
+ dest.setContext( context, false );
+ } finally {
+ if( destSurfaceLocked ) {
+ destSurface.unlockSurface();
+ }
}
- a.setContext( context, false );
} finally {
- if( surfaceLocked ) {
- aSurface.unlockSurface();
- }
+ destUpstreamLock.unlock();
+ }
+ if( null != destUnlockOperation ) {
+ destUnlockOperation.run();
}
+
owner = false;
//
@@ -376,36 +505,36 @@ public class GLEventListenerState {
aGLCmds.add(setViewport);
for(int i=0; i<aSz; i++) {
if( listenersInit[i] ) {
- aGLCmds.add(new ReshapeGLEventListener( listeners[i] ) );
+ aGLCmds.add(new GLDrawableUtil.ReshapeGLEventListener( listeners[i], false ) );
}
}
aGLCmds.add(glFinish);
- a.invoke(aRealized, aGLCmds); // only wait if already realized
+ dest.invoke(destIsRealized, aGLCmds); // only wait if already realized
// 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 );
- a.setGLEventListenerInitState(l, listenersInit[i]);
+ dest.addGLEventListener( l );
+ dest.setGLEventListenerInitState(l, listenersInit[i]);
listeners[i] = null;
}
- if( hasAnimator ) {
+ if( null != destAnim ) {
// prefer already bound animator
- aAnim.add(a);
- if( aPaused ) {
- aAnim.resume();
+ destAnim.add(dest);
+ if( destAnimPaused ) {
+ destAnim.resume();
}
} else if ( null != anim ) {
// use previously bound animator
- anim.add(a); // also handles ECT
+ anim.add(dest); // also handles ECT
if(animStarted) {
anim.start();
}
}
}
- public static final GLRunnable setViewport = new GLRunnable() {
+ private static final GLRunnable setViewport = new GLRunnable() {
@Override
public boolean run(final GLAutoDrawable drawable) {
drawable.getGL().glViewport(0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight());
@@ -413,23 +542,11 @@ public class GLEventListenerState {
}
};
- public static final GLRunnable glFinish = new GLRunnable() {
+ private static final GLRunnable glFinish = new GLRunnable() {
@Override
public boolean run(final GLAutoDrawable drawable) {
drawable.getGL().glFinish();
return true;
}
};
-
- public static class ReshapeGLEventListener implements GLRunnable {
- private final GLEventListener listener;
- public ReshapeGLEventListener(final GLEventListener listener) {
- this.listener = listener;
- }
- @Override
- public boolean run(final GLAutoDrawable drawable) {
- listener.reshape(drawable, 0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight());
- return true;
- }
- }
}
diff --git a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java
index e58d8c64a..eae5948ed 100644
--- a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java
+++ b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java
@@ -638,6 +638,9 @@ public class GLCanvas extends Canvas implements GLAutoDrawable, GLSharedContextS
}
@Override
+ public final RecursiveLock getUpstreamLock() { return lock; }
+
+ @Override
public int getSurfaceWidth() {
return clientArea.width;
}
@@ -755,12 +758,12 @@ public class GLCanvas extends Canvas implements GLAutoDrawable, GLSharedContextS
}
@Override
- public boolean invoke(final boolean wait, final GLRunnable runnable) {
+ public boolean invoke(final boolean wait, final GLRunnable runnable) throws IllegalStateException {
return helper.invoke(this, wait, runnable);
}
@Override
- public boolean invoke(final boolean wait, final List<GLRunnable> runnables) {
+ public boolean invoke(final boolean wait, final List<GLRunnable> runnables) throws IllegalStateException {
return helper.invoke(this, wait, runnables);
}
@@ -879,6 +882,15 @@ public class GLCanvas extends Canvas implements GLAutoDrawable, GLSharedContextS
}
/**
+ * {@inheritDoc}
+ * <p>
+ * Implementation always supports multithreading, hence method always returns <code>true</code>.
+ * </p>
+ */
+ @Override
+ public final boolean isThreadGLCapable() { return true; }
+
+ /**
* Runs the specified action in an SWT compatible thread, which is:
* <ul>
* <li>Mac OSX
diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java b/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java
index c74284299..a2978d6d5 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java
@@ -28,6 +28,7 @@
package com.jogamp.opengl.util;
import javax.media.nativewindow.AbstractGraphicsDevice;
+import javax.media.nativewindow.NativeSurface;
import javax.media.opengl.GLAnimatorControl;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLBase;
@@ -36,7 +37,9 @@ import javax.media.opengl.GLContext;
import javax.media.opengl.GLDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLException;
+import javax.media.opengl.GLRunnable;
+import com.jogamp.common.util.locks.RecursiveLock;
import com.jogamp.opengl.GLEventListenerState;
import jogamp.opengl.Debug;
@@ -45,148 +48,235 @@ import jogamp.opengl.Debug;
* Providing utility functions dealing w/ {@link GLDrawable}s, {@link GLAutoDrawable} and their {@link GLEventListener}.
*/
public class GLDrawableUtil {
- protected static final boolean DEBUG = Debug.debug("GLDrawable");
-
- public static final boolean isAnimatorStartedOnOtherThread(final GLAnimatorControl animatorCtrl) {
- return ( null != animatorCtrl ) ? animatorCtrl.isStarted() && animatorCtrl.getThread() != Thread.currentThread() : false ;
- }
-
- public static final boolean isAnimatorStarted(final GLAnimatorControl animatorCtrl) {
- return ( null != animatorCtrl ) ? animatorCtrl.isStarted() : false ;
- }
-
- public static final boolean isAnimatorAnimatingOnOtherThread(final GLAnimatorControl animatorCtrl) {
- return ( null != animatorCtrl ) ? animatorCtrl.isAnimating() && animatorCtrl.getThread() != Thread.currentThread() : false ;
- }
-
- public static final boolean isAnimatorAnimating(final GLAnimatorControl animatorCtrl) {
- return ( null != animatorCtrl ) ? animatorCtrl.isAnimating() : false ;
- }
-
- /**
- * Moves the designated {@link GLEventListener} from {@link GLAutoDrawable} <code>src</code> to <code>dest</code>.
- * If <code>preserveInitState</code> is <code>true</code>, it's initialized state is preserved
- * and {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)} issued w/ the next {@link GLAutoDrawable#display()} call.
- * <p>
- * Note that it is only legal to pass <code>preserveInitState := true</code>,
- * if the {@link GLContext} of both <code>src</code> and <code>dest</code> are shared, or has itself moved from <code>src</code> to <code>dest</code>.
- * </p>
- * <p>
- * Also note that the caller is encouraged to pause an attached {@link GLAnimatorControl}.
- * </p>
- * @param src
- * @param dest
- * @param listener
- * @param preserveInitState
- */
- public static final void moveGLEventListener(final GLAutoDrawable src, final GLAutoDrawable dest, final GLEventListener listener, final boolean preserveInitState) {
- final boolean initialized = src.getGLEventListenerInitState(listener);
- src.removeGLEventListener(listener);
- dest.addGLEventListener(listener);
- if(preserveInitState && initialized) {
- dest.setGLEventListenerInitState(listener, true);
- dest.invoke(false, new GLEventListenerState.ReshapeGLEventListener(listener));
- } // else .. !init state is default
- }
-
- /**
- * Moves all {@link GLEventListener} from {@link GLAutoDrawable} <code>src</code> to <code>dest</code>.
- * If <code>preserveInitState</code> is <code>true</code>, it's initialized state is preserved
- * and {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)} issued w/ the next {@link GLAutoDrawable#display()} call.
- * <p>
- * Note that it is only legal to pass <code>preserveInitState := true</code>,
- * if the {@link GLContext} of both <code>src</code> and <code>dest</code> are shared, or has itself moved from <code>src</code> to <code>dest</code>.
- * </p>
- * <p>
- * Also note that the caller is encouraged to pause an attached {@link GLAnimatorControl}.
- * </p>
- * @param src
- * @param dest
- * @param listener
- * @param preserveInitState
- */
- public static final void moveAllGLEventListener(final GLAutoDrawable src, final GLAutoDrawable dest, final boolean preserveInitState) {
- for(int count = src.getGLEventListenerCount(); 0<count; count--) {
- final GLEventListener listener = src.getGLEventListener(0);
- moveGLEventListener(src, dest, listener, preserveInitState);
+ protected static final boolean DEBUG = Debug.debug("GLDrawable");
+
+ public static final boolean isAnimatorStartedOnOtherThread(final GLAnimatorControl animatorCtrl) {
+ return ( null != animatorCtrl ) ? animatorCtrl.isStarted() && animatorCtrl.getThread() != Thread.currentThread() : false ;
+ }
+
+ public static final boolean isAnimatorStarted(final GLAnimatorControl animatorCtrl) {
+ return ( null != animatorCtrl ) ? animatorCtrl.isStarted() : false ;
+ }
+
+ public static final boolean isAnimatorAnimatingOnOtherThread(final GLAnimatorControl animatorCtrl) {
+ return ( null != animatorCtrl ) ? animatorCtrl.isAnimating() && animatorCtrl.getThread() != Thread.currentThread() : false ;
+ }
+
+ public static final boolean isAnimatorAnimating(final GLAnimatorControl animatorCtrl) {
+ return ( null != animatorCtrl ) ? animatorCtrl.isAnimating() : false ;
+ }
+
+ /**
+ * {@link GLRunnable} to issue {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int)},
+ * returning <code>true</code> on {@link GLRunnable#run(GLAutoDrawable)}.
+ */
+ public static class ReshapeGLEventListener implements GLRunnable {
+ private final GLEventListener listener;
+ private final boolean displayAfterReshape;
+ /**
+ *
+ * @param listener
+ * @param displayAfterReshape <code>true</code> to issue {@link GLEventListener#display(GLAutoDrawable)}
+ * after {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int)},
+ * otherwise false.
+ */
+ public ReshapeGLEventListener(final GLEventListener listener, final boolean displayAfterReshape) {
+ this.listener = listener;
+ this.displayAfterReshape = displayAfterReshape;
+ }
+ @Override
+ public boolean run(final GLAutoDrawable drawable) {
+ listener.reshape(drawable, 0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight());
+ if( displayAfterReshape ) {
+ listener.display(drawable);
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Moves the designated {@link GLEventListener} from {@link GLAutoDrawable} <code>src</code> to <code>dest</code>.
+ * If <code>preserveInitState</code> is <code>true</code>, it's initialized state is preserved
+ * and {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)} issued w/ the next {@link GLAutoDrawable#display()} call.
+ * <p>
+ * Note that it is only legal to pass <code>preserveInitState := true</code>,
+ * if the {@link GLContext} of both <code>src</code> and <code>dest</code> are shared, or has itself moved from <code>src</code> to <code>dest</code>.
+ * </p>
+ * <p>
+ * Also note that the caller is encouraged to pause an attached {@link GLAnimatorControl}.
+ * </p>
+ * @param src
+ * @param dest
+ * @param listener
+ * @param preserveInitState
+ */
+ public static final void moveGLEventListener(final GLAutoDrawable src, final GLAutoDrawable dest, final GLEventListener listener, final boolean preserveInitState) {
+ final boolean initialized = src.getGLEventListenerInitState(listener);
+ if( preserveInitState ) {
+ src.removeGLEventListener(listener);
+ dest.addGLEventListener(listener);
+ if( initialized ) {
+ dest.setGLEventListenerInitState(listener, true);
+ dest.invoke(false, new ReshapeGLEventListener(listener, true));
+ }
+ } else {
+ src.disposeGLEventListener(listener, true);
+ dest.addGLEventListener(listener);
+ }
+ }
+
+ /**
+ * Moves all {@link GLEventListener} from {@link GLAutoDrawable} <code>src</code> to <code>dest</code>.
+ * If <code>preserveInitState</code> is <code>true</code>, it's initialized state is preserved
+ * and {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)} issued w/ the next {@link GLAutoDrawable#display()} call.
+ * <p>
+ * Note that it is only legal to pass <code>preserveInitState := true</code>,
+ * if the {@link GLContext} of both <code>src</code> and <code>dest</code> are shared, or has itself moved from <code>src</code> to <code>dest</code>.
+ * </p>
+ * <p>
+ * Also note that the caller is encouraged to pause an attached {@link GLAnimatorControl}.
+ * </p>
+ * @param src
+ * @param dest
+ * @param listener
+ * @param preserveInitState
+ */
+ public static final void moveAllGLEventListener(final GLAutoDrawable src, final GLAutoDrawable dest, final boolean preserveInitState) {
+ for(int count = src.getGLEventListenerCount(); 0<count; count--) {
+ final GLEventListener listener = src.getGLEventListener(0);
+ moveGLEventListener(src, dest, listener, preserveInitState);
+ }
+ }
+
+ /**
+ * Swaps the {@link GLContext} and all {@link GLEventListener} between {@link GLAutoDrawable} <code>a</code> and <code>b</code>,
+ * while preserving it's initialized state, resets the GL-Viewport and issuing {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)}.
+ * <p>
+ * The {@link GLAutoDrawable} to {@link GLAnimatorControl} association
+ * is also swapped.
+ * </p>
+ * <p>
+ * If an {@link GLAnimatorControl} is being attached to {@link GLAutoDrawable} <code>a</code> or <code>b</code>
+ * and the current thread is different than {@link GLAnimatorControl#getThread() the animator's thread}, it is paused during the operation.
+ * </p>
+ * <p>
+ * During operation, both {@link GLAutoDrawable auto-drawable's}
+ * {@link GLAutoDrawable#getUpstreamLock() upstream-locks} and {@link GLAutoDrawable#getNativeSurface() surfaces} are locked,
+ * hence atomicity of operation is guaranteed,
+ * see <a href="../../../../javax/media/opengl/GLAutoDrawable.html#locking">GLAutoDrawable Locking</a>.
+ * </p>
+ * @param a
+ * @param b
+ * @throws GLException if the {@link AbstractGraphicsDevice} are incompatible w/ each other.
+ */
+ public static final void swapGLContextAndAllGLEventListener(final GLAutoDrawable a, final GLAutoDrawable b) {
+ final GLEventListenerState gllsA = GLEventListenerState.moveFrom(a, true);
+ final GLEventListenerState gllsB = GLEventListenerState.moveFrom(b, true);
+ final Runnable gllsAUnlockOp = gllsA.getUnlockSurfaceOp();
+ final Runnable gllsBUnlockOp = gllsB.getUnlockSurfaceOp();
+ try {
+ gllsA.moveTo(b, gllsBUnlockOp);
+ gllsB.moveTo(a, gllsAUnlockOp);
+ } finally {
+ // guarantee unlock in case of an exception
+ gllsBUnlockOp.run();
+ gllsAUnlockOp.run();
+ }
}
- }
-
- /**
- * Swaps the {@link GLContext} and all {@link GLEventListener} between {@link GLAutoDrawable} <code>a</code> and <code>b</code>,
- * while preserving it's initialized state, resets the GL-Viewport and issuing {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)}.
- * <p>
- * The {@link GLAutoDrawable} to {@link GLAnimatorControl} association
- * is also swapped.
- * </p>
- * <p>
- * If an {@link GLAnimatorControl} is being attached to {@link GLAutoDrawable} <code>a</code> or <code>b</code>
- * and the current thread is different than {@link GLAnimatorControl#getThread() the animator's thread}, it is paused during the operation.
- * </p>
- * @param a
- * @param b
- * @throws GLException if the {@link AbstractGraphicsDevice} are incompatible w/ each other.
- */
- public static final void swapGLContextAndAllGLEventListener(final GLAutoDrawable a, final GLAutoDrawable b) {
- final GLEventListenerState gllsA = GLEventListenerState.moveFrom(a);
- final GLEventListenerState gllsB = GLEventListenerState.moveFrom(b);
-
- gllsA.moveTo(b);
- gllsB.moveTo(a);
- }
-
- /**
- * Swaps the {@link GLContext} of given {@link GLAutoDrawable}
- * and {@link GLAutoDrawable#disposeGLEventListener(GLEventListener, boolean) disposes}
- * each {@link GLEventListener} w/o removing it.
- * <p>
- * The GL-Viewport is reset and {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)} issued implicit.
- * </p>
- * <p>
- * If an {@link GLAnimatorControl} is being attached to GLAutoDrawable src or dest and the current thread is different
- * than {@link GLAnimatorControl#getThread() the animator's thread}, it is paused during the operation.
- * </p>
- * @param src
- * @param dest
- */
- public static final void swapGLContext(final GLAutoDrawable src, final GLAutoDrawable dest) {
- final GLAnimatorControl aAnim = src.getAnimator();
- final GLAnimatorControl bAnim = dest.getAnimator();
- final boolean aIsPaused = isAnimatorAnimatingOnOtherThread(aAnim) && aAnim.pause();
- final boolean bIsPaused = isAnimatorAnimatingOnOtherThread(bAnim) && bAnim.pause();
-
- for(int i = src.getGLEventListenerCount() - 1; 0 <= i; i--) {
- src.disposeGLEventListener(src.getGLEventListener(i), false);
+
+ /**
+ * Swaps the {@link GLContext} of given {@link GLAutoDrawable}
+ * and {@link GLAutoDrawable#disposeGLEventListener(GLEventListener, boolean) disposes}
+ * each {@link GLEventListener} w/o removing it.
+ * <p>
+ * The GL-Viewport is reset and {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)} issued implicit.
+ * </p>
+ * <p>
+ * If an {@link GLAnimatorControl} is being attached to GLAutoDrawable src or dest and the current thread is different
+ * than {@link GLAnimatorControl#getThread() the animator's thread}, it is paused during the operation.
+ * </p>
+ * <p>
+ * During operation, both {@link GLAutoDrawable auto-drawable's}
+ * {@link GLAutoDrawable#getUpstreamLock() upstream-locks} and {@link GLAutoDrawable#getNativeSurface() surfaces} are locked,
+ * hence atomicity of operation is guaranteed,
+ * see <a href="../../../../javax/media/opengl/GLAutoDrawable.html#locking">GLAutoDrawable Locking</a>.
+ * </p>
+ * @param a
+ * @param b
+ */
+ public static final void swapGLContext(final GLAutoDrawable a, final GLAutoDrawable b) {
+ final GLAnimatorControl aAnim = a.getAnimator();
+ final GLAnimatorControl bAnim = b.getAnimator();
+ final boolean aIsPaused = isAnimatorAnimatingOnOtherThread(aAnim) && aAnim.pause();
+ final boolean bIsPaused = isAnimatorAnimatingOnOtherThread(bAnim) && bAnim.pause();
+
+ final RecursiveLock aUpstreamLock = a.getUpstreamLock();
+ final RecursiveLock bUpstreamLock = b.getUpstreamLock();
+ aUpstreamLock.lock();
+ bUpstreamLock.lock();
+ try {
+ final NativeSurface aSurface = a.getNativeSurface();
+ final boolean aSurfaceLocked = NativeSurface.LOCK_SURFACE_NOT_READY < aSurface.lockSurface();
+ if( a.isRealized() && !aSurfaceLocked ) {
+ throw new GLException("Could not lock realized a surface "+a);
+ }
+ final NativeSurface bSurface = b.getNativeSurface();
+ final boolean bSurfaceLocked = NativeSurface.LOCK_SURFACE_NOT_READY < bSurface.lockSurface();
+ if( b.isRealized() && !bSurfaceLocked ) {
+ throw new GLException("Could not lock realized b surface "+b);
+ }
+ try {
+ for(int i = a.getGLEventListenerCount() - 1; 0 <= i; i--) {
+ a.disposeGLEventListener(a.getGLEventListener(i), false);
+ }
+ for(int i = b.getGLEventListenerCount() - 1; 0 <= i; i--) {
+ b.disposeGLEventListener(b.getGLEventListener(i), false);
+ }
+ b.setContext( a.setContext( b.getContext(), false ), false );
+
+ } finally {
+ if( bSurfaceLocked ) {
+ bSurface.unlockSurface();
+ }
+ if( aSurfaceLocked ) {
+ aSurface.unlockSurface();
+ }
+ }
+ } finally {
+ bUpstreamLock.unlock();
+ aUpstreamLock.unlock();
+ }
+ a.invoke(true, setViewport);
+ b.invoke(true, setViewport);
+ if(aIsPaused) { aAnim.resume(); }
+ if(bIsPaused) { bAnim.resume(); }
}
- for(int i = dest.getGLEventListenerCount() - 1; 0 <= i; i--) {
- dest.disposeGLEventListener(dest.getGLEventListener(i), false);
+
+ private static final GLRunnable setViewport = new GLRunnable() {
+ @Override
+ public boolean run(final GLAutoDrawable drawable) {
+ drawable.getGL().glViewport(0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight());
+ return false; // issue re-display w/ new viewport!
+ }
+ };
+
+ /**
+ * Determines whether the chosen {@link GLCapabilitiesImmutable}
+ * requires a {@link GLDrawable#swapBuffers() swap-buffers}
+ * before reading pixels.
+ * <p>
+ * Usually one uses the {@link GLBase#getDefaultReadBuffer() default-read-buffer}
+ * in which case {@link GLDrawable#swapBuffers() swap-buffers} shall happen <b>after</b> calling reading pixels, the default.
+ * </p>
+ * <p>
+ * However, <i>multisampling</i> offscreen {@link javax.media.opengl.GLFBODrawable}s
+ * utilize {@link GLDrawable#swapBuffers() swap-buffers} to <i>downsample</i>
+ * the multisamples into the readable sampling sink.
+ * In this case, we require {@link GLDrawable#swapBuffers() swap-buffers} <b>before</b> reading pixels.
+ * </p>
+ * @return chosenCaps.isFBO() && chosenCaps.getSampleBuffers()
+ */
+ public static final boolean swapBuffersBeforeRead(final GLCapabilitiesImmutable chosenCaps) {
+ return chosenCaps.isFBO() && chosenCaps.getSampleBuffers();
}
- dest.setContext( src.setContext( dest.getContext(), false ), false );
-
- src.invoke(true, GLEventListenerState.setViewport);
- dest.invoke(true, GLEventListenerState.setViewport);
-
- if(aIsPaused) { aAnim.resume(); }
- if(bIsPaused) { bAnim.resume(); }
- }
-
- /**
- * Determines whether the chosen {@link GLCapabilitiesImmutable}
- * requires a {@link GLDrawable#swapBuffers() swap-buffers}
- * before reading pixels.
- * <p>
- * Usually one uses the {@link GLBase#getDefaultReadBuffer() default-read-buffer}
- * in which case {@link GLDrawable#swapBuffers() swap-buffers} shall happen <b>after</b> calling reading pixels, the default.
- * </p>
- * <p>
- * However, <i>multisampling</i> offscreen {@link javax.media.opengl.GLFBODrawable}s
- * utilize {@link GLDrawable#swapBuffers() swap-buffers} to <i>downsample</i>
- * the multisamples into the readable sampling sink.
- * In this case, we require {@link GLDrawable#swapBuffers() swap-buffers} <b>before</b> reading pixels.
- * </p>
- * @return chosenCaps.isFBO() && chosenCaps.getSampleBuffers()
- */
- public static final boolean swapBuffersBeforeRead(final GLCapabilitiesImmutable chosenCaps) {
- return chosenCaps.isFBO() && chosenCaps.getSampleBuffers();
- }
}