aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2013-01-11 08:13:24 +0100
committerSven Gothel <[email protected]>2013-01-11 08:13:24 +0100
commit224fab1b2c71464826594740022fdcbe278867dc (patch)
treefb4292f8cd8b8fa2664c04005ba2ddde630d159c /src
parent6fd9c3d84e1758ae27cd10a89237a558460ca1fb (diff)
GLAutoDrawable/AnimatorBase: Add ExclusiveContextThread (ECT) feature; AnimatorBase: Add setModeBits/MODE_EXPECT_AWT_RENDERING_THREAD; FPSAnimator: Make transactions deterministic.
ExclusiveContextThread (ECT) allows user to dedicate a GLContext to a given thread. Only the ECT will be allowed to claim the GLContext, hence releasing must be done on the ECT itself. The core feature is accessible via GLAutoDrawable, while it can be conveniently enabled and disabled via an AnimatorBase implementation. The latter ensures it's being released on the ECT and waits for the result. Note that ECT cannot be guaranteed to work correctly w/ native (heavyweight) AWT components due to resource locking and AWT-EDT access. This is disabled in all new tests per default and noted on the API doc. Note: 'Animator transaction' == start(), stop(), pause(), resume(). - Add ExclusiveContextThread (ECT) feature - GLAutoDrawable NEW: - Thread setExclusiveContextThread(Thread t) - Thread getExclusiveContextThread() - AnimatorBase NEW: - Thread setExclusiveContext(Thread t) - boolean setExclusiveContext(boolean enable) - boolean isExclusiveContextEnabled() - Thread getExclusiveContextThread() - AnimatorBase: Add setModeBits/MODE_EXPECT_AWT_RENDERING_THREAD Allows user to pre-determine whether AWT rendering is expected before starting the animator. If AWT is excluded, a more simple and transaction correct impl. will be used. - FPSAnimator: Make transactions deterministic. FPSAnimator previously did not ensure whether a transaction was completed. A deterministic transaction is required to utilize ECT. FPSAnimator now uses same mechanism like Animator to ensure completeness, i.e. Condition and 'finishLifecycleAction(..)'. Both are moved to AnimatorBase. Tested manually on Linux/NV, Linux/AMD, Windows/NV and OSX/NV. - All new tests validated correctness. - All new tests shows an performance increase of ~3x w/ single GLWindow, where multiple GLWindows don't show a perf. increase.
Diffstat (limited to 'src')
-rw-r--r--src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java10
-rw-r--r--src/jogl/classes/com/jogamp/opengl/util/Animator.java187
-rw-r--r--src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java458
-rw-r--r--src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java307
-rw-r--r--src/jogl/classes/javax/media/opengl/GLAnimatorControl.java21
-rw-r--r--src/jogl/classes/javax/media/opengl/GLAutoDrawable.java45
-rw-r--r--src/jogl/classes/javax/media/opengl/GLContext.java8
-rw-r--r--src/jogl/classes/javax/media/opengl/awt/GLCanvas.java10
-rw-r--r--src/jogl/classes/javax/media/opengl/awt/GLJPanel.java10
-rw-r--r--src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java30
-rw-r--r--src/jogl/classes/jogamp/opengl/GLDrawableHelper.java421
-rwxr-xr-xsrc/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtAppletBase.java5
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/acore/ExclusiveContextBase00.java420
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/acore/ExclusiveContextBase00AWT.java161
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/acore/ExclusiveContextBase00NEWT.java94
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/acore/ExclusiveContextBase10.java213
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/acore/ExclusiveContextBase10AWT.java161
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/acore/ExclusiveContextBase10NEWT.java94
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/acore/InitConcurrentBaseNEWT.java2
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/acore/TestExclusiveContext01VSyncAnimAWT.java70
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/acore/TestExclusiveContext01VSyncAnimNEWT.java67
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/acore/TestExclusiveContext02FPSAnimAWT.java70
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/acore/TestExclusiveContext02FPSAnimNEWT.java67
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/acore/TestExclusiveContext11VSyncAnimNEWT.java66
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/acore/TestExclusiveContext12FPSAnimNEWT.java67
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/demos/es1/newt/TestGearsES1NEWT.java9
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java87
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/RedSquareES2.java24
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/awt/TestGearsES2AWT.java28
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java40
30 files changed, 2782 insertions, 470 deletions
diff --git a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java
index 4ba4def9a..80e1aa80d 100644
--- a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java
+++ b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java
@@ -649,6 +649,16 @@ public class GLCanvas extends Canvas implements GLAutoDrawable {
}
@Override
+ public final Thread setExclusiveContextThread(Thread t) throws GLException {
+ return helper.setExclusiveContextThread(t, context);
+ }
+
+ @Override
+ public final Thread getExclusiveContextThread() {
+ return helper.getExclusiveContextThread();
+ }
+
+ @Override
public boolean getAutoSwapBufferMode() {
return helper.getAutoSwapBufferMode();
}
diff --git a/src/jogl/classes/com/jogamp/opengl/util/Animator.java b/src/jogl/classes/com/jogamp/opengl/util/Animator.java
index ddbc66fa3..10f43a0c1 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/Animator.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/Animator.java
@@ -41,7 +41,7 @@
package com.jogamp.opengl.util;
import javax.media.opengl.GLAutoDrawable;
-
+import javax.media.opengl.GLException;
/** <P> An Animator can be attached to one or more {@link
GLAutoDrawable}s to drive their display() methods in a loop. </P>
@@ -57,10 +57,7 @@ import javax.media.opengl.GLAutoDrawable;
* Call {@link #stop() } to terminate the animation and it's execution thread.
* </p>
*/
-public class Animator extends AnimatorBase {
- /** timeout in milliseconds, 15 frames @ 60Hz = 240ms, limiting {@link #finishLifecycleAction(Condition)} */
- private static final long TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION = 15*16;
-
+public class Animator extends AnimatorBase {
protected ThreadGroup threadGroup;
private Runnable runnable;
private boolean runAsFastAsPossible;
@@ -68,6 +65,9 @@ public class Animator extends AnimatorBase {
protected boolean pauseIssued;
protected volatile boolean stopIssued;
+ /**
+ * Creates a new, empty Animator.
+ */
public Animator() {
super();
if(DEBUG) {
@@ -75,25 +75,38 @@ public class Animator extends AnimatorBase {
}
}
+ /**
+ * Creates a new Animator w/ an associated ThreadGroup.
+ */
public Animator(ThreadGroup tg) {
super();
- threadGroup = tg;
-
+ setThreadGroup(tg);
if(DEBUG) {
System.err.println("Animator created, ThreadGroup: "+threadGroup);
}
}
- /** Creates a new Animator for a particular drawable. */
+ /**
+ * Creates a new Animator for a particular drawable.
+ */
public Animator(GLAutoDrawable drawable) {
super();
add(drawable);
+ if(DEBUG) {
+ System.err.println("Animator created, w/ "+drawable);
+ }
}
- /** Creates a new Animator for a particular drawable. */
+ /**
+ * Creates a new Animator w/ an associated ThreadGroup for a particular drawable.
+ */
public Animator(ThreadGroup tg, GLAutoDrawable drawable) {
- this(tg);
+ super();
+ setThreadGroup(tg);
add(drawable);
+ if(DEBUG) {
+ System.err.println("Animator created, ThreadGroup: "+threadGroup+" and "+drawable);
+ }
}
protected String getBaseName(String prefix) {
@@ -114,7 +127,7 @@ public class Animator extends AnimatorBase {
stateSync.unlock();
}
}
-
+
private final void setIsAnimatingSynced(boolean v) {
stateSync.lock();
try {
@@ -126,36 +139,39 @@ public class Animator extends AnimatorBase {
class MainLoop implements Runnable {
public String toString() {
- return "[started "+isStartedImpl()+", animating "+isAnimatingImpl()+", paused "+isPausedImpl()+", drawable "+drawables.size()+"]";
+ return "[started "+isStartedImpl()+", animating "+isAnimatingImpl()+", paused "+isPausedImpl()+", drawable "+drawables.size()+", drawablesEmpty "+drawablesEmpty+"]";
}
public void run() {
try {
- synchronized (Animator.this) {
- if(DEBUG) {
- System.err.println("Animator start:" + Thread.currentThread() + ": " + toString());
- }
- fpsCounter.resetFPSCounter();
- animThread = Thread.currentThread();
- setIsAnimatingSynced(false); // barrier
- Animator.this.notifyAll();
+ if(DEBUG) {
+ System.err.println("Animator start:" + Thread.currentThread() + ": " + toString());
}
+ fpsCounter.resetFPSCounter();
+ animThread = Thread.currentThread();
+ setIsAnimatingSynced(false); // barrier
+ // 'waitForStartedCondition' wake-up is handled below!
while (!stopIssued) {
synchronized (Animator.this) {
- // Don't consume CPU unless there is work to be done and not paused
+ // Pause; Also don't consume CPU unless there is work to be done and not paused
+ boolean ectCleared = false;
while (!stopIssued && (pauseIssued || drawablesEmpty)) {
boolean wasPaused = pauseIssued;
if (DEBUG) {
System.err.println("Animator pause:" + Thread.currentThread() + ": " + toString());
}
+ if ( exclusiveContext && !drawablesEmpty && !ectCleared ) {
+ ectCleared = true;
+ setDrawablesExclCtxState(false);
+ display(); // propagate exclusive change!
+ }
setIsAnimatingSynced(false); // barrier
Animator.this.notifyAll();
try {
Animator.this.wait();
} catch (InterruptedException e) {
}
-
if (wasPaused) {
// resume from pause -> reset counter
fpsCounter.resetFPSCounter();
@@ -165,9 +181,12 @@ public class Animator extends AnimatorBase {
}
}
if (!stopIssued && !isAnimating) {
- // resume from pause or drawablesEmpty,
+ // Wakes up 'waitForStartedCondition' sync
+ // - and -
+ // Resume from pause or drawablesEmpty,
// implies !pauseIssued and !drawablesEmpty
- setIsAnimatingSynced(true);
+ setIsAnimatingSynced(true); // barrier
+ setDrawablesExclCtxState(exclusiveContext);
Animator.this.notifyAll();
}
} // sync Animator.this
@@ -180,6 +199,10 @@ public class Animator extends AnimatorBase {
}
}
} finally {
+ if( exclusiveContext && !drawablesEmpty ) {
+ setDrawablesExclCtxState(false);
+ display(); // propagate exclusive change!
+ }
synchronized (Animator.this) {
if(DEBUG) {
System.err.println("Animator stop " + Thread.currentThread() + ": " + toString());
@@ -194,18 +217,6 @@ public class Animator extends AnimatorBase {
}
}
- private final boolean isStartedImpl() {
- return animThread != null ;
- }
- public final boolean isStarted() {
- stateSync.lock();
- try {
- return animThread != null ;
- } finally {
- stateSync.unlock();
- }
- }
-
private final boolean isAnimatingImpl() {
return animThread != null && isAnimating ;
}
@@ -230,39 +241,19 @@ public class Animator extends AnimatorBase {
}
}
- interface Condition {
- /**
- * @return true if branching (cont waiting, action), otherwise false
- */
- boolean result();
- }
-
- private synchronized void finishLifecycleAction(Condition condition) {
- // It's hard to tell whether the thread which changes the lifecycle has
- // dependencies on the Animator's internal thread. Currently we
- // use a couple of heuristics to determine whether we should do
- // the blocking wait().
- final boolean blocking = impl.blockUntilDone(animThread);
- long remaining = blocking ? TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION : 0;
- while (remaining>0 && condition.result()) {
- long td = System.currentTimeMillis();
- try {
- wait(remaining);
- } catch (InterruptedException ie) { }
- remaining -= (System.currentTimeMillis() - td) ;
- }
- if(DEBUG) {
- if(remaining<0) {
- System.err.println("finishLifecycleAction(" + condition.getClass().getName() + "): ++++++ timeout reached ++++++ " + Thread.currentThread().getName());
- }
- System.err.println("finishLifecycleAction(" + condition.getClass().getName() + "): finished "+
- "- blocking "+blocking+
- ", waited " + (blocking ? ( TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION - remaining ) : 0 ) + "/" + TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION +
- ", started: " + isStartedImpl() +", animating: " + isAnimatingImpl() +
- ", paused: " + isPausedImpl() + ", drawables " + drawables.size() + " - " + Thread.currentThread().getName());
+ /**
+ * Set a {@link ThreadGroup} for the {@link #getThread() animation thread}.
+ *
+ * @param tg the {@link ThreadGroup}
+ * @throws GLException if the animator has already been started
+ */
+ public synchronized void setThreadGroup(ThreadGroup tg) throws GLException {
+ if ( isStartedImpl() ) {
+ throw new GLException("Animator already started.");
}
+ threadGroup = tg;
}
-
+
public synchronized boolean start() {
if ( isStartedImpl() ) {
return false;
@@ -284,74 +275,48 @@ public class Animator extends AnimatorBase {
System.err.println("Animator "+ct.getName()+"[daemon "+ct.isDaemon()+"]: starting "+thread.getName()+"[daemon "+thread.isDaemon()+"]");
}
thread.start();
- finishLifecycleAction(waitForStartedCondition);
- return true;
+ return finishLifecycleAction(waitForStartedCondition, 0);
}
-
- private class WaitForStartedCondition implements Condition {
- public boolean result() {
+ private final Condition waitForStartedCondition = new Condition() {
+ public boolean eval() {
return !isStartedImpl() || (!drawablesEmpty && !isAnimating) ;
- }
- }
- Condition waitForStartedCondition = new WaitForStartedCondition();
+ } };
public synchronized boolean stop() {
if ( !isStartedImpl() ) {
return false;
}
stopIssued = true;
- notifyAll();
- finishLifecycleAction(waitForStoppedCondition);
- return true;
+ return finishLifecycleAction(waitForStoppedCondition, 0);
}
- private class WaitForStoppedCondition implements Condition {
- public boolean result() {
+ private final Condition waitForStoppedCondition = new Condition() {
+ public boolean eval() {
return isStartedImpl();
- }
- }
- Condition waitForStoppedCondition = new WaitForStoppedCondition();
+ } };
public synchronized boolean pause() {
if ( !isStartedImpl() || pauseIssued ) {
return false;
}
- stateSync.lock();
- try {
- pauseIssued = true;
- } finally {
- stateSync.unlock();
- }
- notifyAll();
- finishLifecycleAction(waitForPausedCondition);
- return true;
+ pauseIssued = true;
+ return finishLifecycleAction(waitForPausedCondition, 0);
}
- private class WaitForPausedCondition implements Condition {
- public boolean result() {
+ private final Condition waitForPausedCondition = new Condition() {
+ public boolean eval() {
// end waiting if stopped as well
return isAnimating && isStartedImpl();
- }
- }
- Condition waitForPausedCondition = new WaitForPausedCondition();
+ } };
public synchronized boolean resume() {
if ( !isStartedImpl() || !pauseIssued ) {
return false;
}
- stateSync.lock();
- try {
- pauseIssued = false;
- } finally {
- stateSync.unlock();
- }
- notifyAll();
- finishLifecycleAction(waitForResumeCondition);
- return true;
+ pauseIssued = false;
+ return finishLifecycleAction(waitForResumeCondition, 0);
}
- private class WaitForResumeCondition implements Condition {
- public boolean result() {
+ private final Condition waitForResumeCondition = new Condition() {
+ public boolean eval() {
// end waiting if stopped as well
return !drawablesEmpty && !isAnimating && isStartedImpl();
- }
- }
- Condition waitForResumeCondition = new WaitForResumeCondition();
+ } };
}
diff --git a/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java b/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java
index 46fc1d991..bda716e97 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java
@@ -38,6 +38,7 @@ import java.util.ArrayList;
import javax.media.opengl.GLAnimatorControl;
import javax.media.opengl.GLAutoDrawable;
+import javax.media.opengl.GLException;
import javax.media.opengl.GLProfile;
/**
@@ -51,69 +52,174 @@ import javax.media.opengl.GLProfile;
*/
public abstract class AnimatorBase implements GLAnimatorControl {
protected static final boolean DEBUG = Debug.debug("Animator");
-
- private static int animatorCount = 0;
-
+
+ /** A 1s timeout while waiting for a native action response, limiting {@link #finishLifecycleAction(Condition, long)} */
+ protected static final long TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION = 1000;
+
+ protected static final long POLLP_WAIT_FOR_FINISH_LIFECYCLE_ACTION = 32; // 2 frames @ 60Hz
+
+ /**
+ * If present in <code>modeBits</code> field and
+ * {@link GLProfile#isAWTAvailable() AWT is available},
+ * implementation is aware of the AWT EDT, otherwise not.
+ * <p>
+ * This is the <i>default</i>.
+ * </p>
+ * @see #setModeBits(boolean, int)
+ */
+ public static final int MODE_EXPECT_AWT_RENDERING_THREAD = 1 << 0;
+
public interface AnimatorImpl {
void display(ArrayList<GLAutoDrawable> drawables, boolean ignoreExceptions, boolean printExceptions);
boolean blockUntilDone(Thread thread);
}
- protected ArrayList<GLAutoDrawable> drawables = new ArrayList<GLAutoDrawable>();
- protected boolean drawablesEmpty;
+ protected int modeBits;
protected AnimatorImpl impl;
protected String baseName;
+
+ protected ArrayList<GLAutoDrawable> drawables = new ArrayList<GLAutoDrawable>();
+ protected boolean drawablesEmpty;
protected Thread animThread;
protected boolean ignoreExceptions;
protected boolean printExceptions;
+ protected boolean exclusiveContext;
+ protected Thread userExclusiveContextThread;
protected FPSCounterImpl fpsCounter = new FPSCounterImpl();
protected RecursiveLock stateSync = LockFactory.createRecursiveLock();
-
- /** Creates a new, empty Animator. */
- public AnimatorBase() {
- if(GLProfile.isAWTAvailable()) {
+
+ private final static Class<?> awtAnimatorImplClazz;
+ static {
+ GLProfile.initSingleton();
+ if( GLProfile.isAWTAvailable() ) {
+ Class<?> clazz;
try {
- impl = (AnimatorImpl) Class.forName("com.jogamp.opengl.util.AWTAnimatorImpl").newInstance();
- baseName = "AWTAnimator";
- } catch (Exception e) { e.printStackTrace(); }
- }
- if(null==impl) {
- impl = new DefaultAnimatorImpl();
- baseName = "Animator";
- }
- synchronized (Animator.class) {
- animatorCount++;
- baseName = baseName.concat("-"+animatorCount);
- drawablesEmpty = true;
+ clazz = Class.forName("com.jogamp.opengl.util.AWTAnimatorImpl");
+ } catch (Exception e) {
+ clazz = null;
+ }
+ awtAnimatorImplClazz = clazz;
+ } else {
+ awtAnimatorImplClazz = null;
}
}
+ /**
+ * Creates a new, empty Animator instance
+ * while expecting an AWT rendering thread if AWT is available.
+ *
+ * @see GLProfile#isAWTAvailable()
+ */
+ public AnimatorBase() {
+ modeBits = MODE_EXPECT_AWT_RENDERING_THREAD; // default!
+ drawablesEmpty = true;
+ }
+
+ /**
+ * Initializes implementation details post setup,
+ * invoked at {@link #add(GLAutoDrawable)}, {@link #start()}, ..
+ * <p>
+ * Operation is a NOP if <code>force</code> is <code>false</code>
+ * and this instance is already initialized.
+ * </p>
+ *
+ * @throws GLException if Animator is {@link #isStarted()}
+ */
+ protected void initImpl(boolean force) {
+ if( force || null == impl ) {
+ if( 0 != ( MODE_EXPECT_AWT_RENDERING_THREAD & modeBits ) && null != awtAnimatorImplClazz ) {
+ try {
+ impl = (AnimatorImpl) awtAnimatorImplClazz.newInstance();
+ baseName = getBaseName("AWT");
+ } catch (Exception e) { e.printStackTrace(); }
+ }
+ if( null == impl ) {
+ impl = new DefaultAnimatorImpl();
+ baseName = getBaseName("");
+ }
+ if(DEBUG) {
+ System.err.println("Animator.initImpl: baseName "+baseName+", implClazz "+impl.getClass().getName()+" - "+toString()+" - "+Thread.currentThread().getName());
+ }
+ }
+ }
protected abstract String getBaseName(String prefix);
- public synchronized void add(GLAutoDrawable drawable) {
+ /**
+ * Enables or disables the given <code>bitValues</code>
+ * in this Animators <code>modeBits</code>.
+ * @param enable
+ * @param bitValues
+ *
+ * @throws GLException if Animator is {@link #isStarted()}
+ * @see AnimatorBase#MODE_EXPECT_AWT_RENDERING_THREAD
+ */
+ public synchronized void setModeBits(boolean enable, int bitValues) throws GLException {
+ if( isStarted() ) {
+ throw new GLException("Animator already started");
+ }
+ final int _oldModeBits = modeBits;
+ if(enable) {
+ modeBits |= bitValues;
+ } else {
+ modeBits &= ~bitValues;
+ }
+ if( _oldModeBits != modeBits ) {
+ initImpl(true);
+ }
+ }
+ public synchronized int getModeBits() { return modeBits; }
+
+
+ @Override
+ public synchronized void add(final GLAutoDrawable drawable) {
if(DEBUG) {
- System.err.println("Animator add: "+drawable.hashCode()+" - "+Thread.currentThread().getName());
+ System.err.println("Animator add: 0x"+Integer.toHexString(drawable.hashCode())+" - "+toString()+" - "+Thread.currentThread().getName());
}
+ if( drawables.contains(drawable) ) {
+ throw new IllegalArgumentException("Drawable already added to animator: "+this+", "+drawable);
+ }
+ initImpl(false);
boolean paused = pause();
+ if( isStarted() ) {
+ drawable.setExclusiveContextThread( exclusiveContext ? getExclusiveContextThread() : null ); // if already running ..
+ }
drawables.add(drawable);
drawablesEmpty = drawables.size() == 0;
drawable.setAnimator(this);
if(paused) {
resume();
}
- if(impl.blockUntilDone(animThread)) {
- while(isStarted() && !isPaused() && !isAnimating()) {
- try {
- wait();
- } catch (InterruptedException ie) { }
- }
+ final Condition waitForAnimatingAndECTCondition = new Condition() {
+ public boolean eval() {
+ final Thread dect = drawable.getExclusiveContextThread();
+ return isStarted() && !isPaused() && !isAnimating() && ( exclusiveContext && null == dect || !exclusiveContext && null != dect );
+ } };
+ final boolean res = finishLifecycleAction(waitForAnimatingAndECTCondition, 0);
+ if(DEBUG) {
+ System.err.println("Animator add: Wait for Animating/ECT OK: "+res+", "+toString()+", dect "+drawable.getExclusiveContextThread());
}
notifyAll();
}
- public synchronized void remove(GLAutoDrawable drawable) {
+ @Override
+ public synchronized void remove(final GLAutoDrawable drawable) {
if(DEBUG) {
- System.err.println("Animator remove: "+drawable.hashCode()+" - "+Thread.currentThread().getName() + ": "+toString());
+ System.err.println("Animator remove: 0x"+Integer.toHexString(drawable.hashCode())+" - "+toString()+" - "+Thread.currentThread().getName());
+ }
+ if( !drawables.contains(drawable) ) {
+ throw new IllegalArgumentException("Drawable not added to animator: "+this+", "+drawable);
+ }
+
+ if( exclusiveContext && isAnimating() ) {
+ drawable.setExclusiveContextThread( null );
+ final Condition waitForNullECTCondition = new Condition() {
+ public boolean eval() {
+ return null != drawable.getExclusiveContextThread();
+ } };
+ final boolean res = finishLifecycleAction(waitForNullECTCondition, POLLP_WAIT_FOR_FINISH_LIFECYCLE_ACTION);
+ if(DEBUG) {
+ System.err.println("Animator remove: Wait for Null-ECT OK: "+res+", "+toString()+", dect "+drawable.getExclusiveContextThread());
+ }
}
boolean paused = pause();
@@ -123,14 +229,202 @@ public abstract class AnimatorBase implements GLAnimatorControl {
if(paused) {
resume();
}
- if(impl.blockUntilDone(animThread)) {
- while(isStarted() && drawablesEmpty && isAnimating()) {
+ final boolean res = finishLifecycleAction(waitForNotAnimatingIfEmptyCondition, 0);
+ if(DEBUG) {
+ System.err.println("Animator remove: Wait for !Animating-if-empty OK: "+res+", "+toString());
+ }
+ notifyAll();
+ }
+ private final Condition waitForNotAnimatingIfEmptyCondition = new Condition() {
+ public boolean eval() {
+ return isStarted() && drawablesEmpty && isAnimating();
+ } };
+
+
+ /**
+ * Dedicate all {@link GLAutoDrawable}'s context to the given exclusive context thread.
+ * <p>
+ * The given thread will be exclusive to all {@link GLAutoDrawable}'s context while {@link #isAnimating()}.
+ * </p>
+ * <p>
+ * If already started and disabling, method waits
+ * until change is propagated to all {@link GLAutoDrawable} if not
+ * called from the animator thread or {@link #getExclusiveContextThread() exclusive context thread}.
+ * </p>
+ * <p>
+ * Note: Utilizing this feature w/ AWT could lead to an AWT-EDT deadlock, depending on the AWT implementation.
+ * Hence it is advised not to use it with native AWT GLAutoDrawable like GLCanvas.
+ * </p>
+ *
+ * @param enable
+ * @return previous value
+ * @see #setExclusiveContext(boolean)
+ * @see #getExclusiveContextThread()
+ * @see #isExclusiveContextEnabled()
+ */
+ // @Override
+ public final Thread setExclusiveContext(Thread t) {
+ final Thread old;
+ final boolean enable = null != t;
+ stateSync.lock();
+ try {
+ old = userExclusiveContextThread;
+ if( enable && t != animThread ) { // disable: will be cleared at end after propagation && filter out own animThread usae
+ userExclusiveContextThread=t;
+ }
+ } finally {
+ stateSync.unlock();
+ }
+ setExclusiveContext(enable);
+ return old;
+ }
+
+ /**
+ * Dedicate all {@link GLAutoDrawable}'s context to this animator thread.
+ * <p>
+ * The given thread will be exclusive to all {@link GLAutoDrawable}'s context while {@link #isAnimating()}.
+ * </p>
+ * <p>
+ * If already started and disabling, method waits
+ * until change is propagated to all {@link GLAutoDrawable} if not
+ * called from the animator thread or {@link #getExclusiveContextThread() exclusive context thread}.
+ * </p>
+ * <p>
+ * Note: Utilizing this feature w/ AWT could lead to an AWT-EDT deadlock, depending on the AWT implementation.
+ * Hence it is advised not to use it with native AWT GLAutoDrawable like GLCanvas.
+ * </p>
+ *
+ * @param enable
+ * @return previous value
+ * @see #setExclusiveContext(Thread)
+ * @see #getExclusiveContextThread()
+ * @see #isExclusiveContextEnabled()
+ */
+ // @Override
+ public final boolean setExclusiveContext(boolean enable) {
+ final boolean propagateState;
+ final boolean oldExclusiveContext;
+ final Thread _exclusiveContextThread;
+ synchronized (AnimatorBase.this) {
+ propagateState = isStarted() && !drawablesEmpty;
+ _exclusiveContextThread = userExclusiveContextThread;
+ oldExclusiveContext = exclusiveContext;
+ exclusiveContext = enable;
+ if(DEBUG) {
+ System.err.println("AnimatorBase.setExclusiveContextThread: "+oldExclusiveContext+" -> "+exclusiveContext+", propagateState "+propagateState+", "+this);
+ }
+ }
+ final Thread dECT = enable ? ( null != userExclusiveContextThread ? userExclusiveContextThread : animThread ) : null ;
+ if( propagateState ) {
+ setDrawablesExclCtxState(enable);
+ if( !enable ) {
+ if( Thread.currentThread() == getThread() || Thread.currentThread() == _exclusiveContextThread ) {
+ display();
+ } else {
+ final boolean resumed = isAnimating() ? false : resume();
+ int counter = 10;
+ while( 0<counter && isAnimating() && !validateDrawablesExclCtxState(dECT) ) {
+ try {
+ Thread.sleep(20);
+ } catch (InterruptedException e) { }
+ counter--;
+ }
+ if(resumed) {
+ pause();
+ }
+ }
+ stateSync.lock();
try {
- wait();
- } catch (InterruptedException ie) { }
+ userExclusiveContextThread=null;
+ } finally {
+ stateSync.unlock();
+ }
}
}
- notifyAll();
+ if(DEBUG) {
+ System.err.println("AnimatorBase.setExclusiveContextThread: all-GLAD Ok: "+validateDrawablesExclCtxState(dECT)+", "+this);
+ }
+ return oldExclusiveContext;
+ }
+
+ /**
+ * Returns <code>true</code>, if the exclusive context thread is enabled, otherwise <code>false</code>.
+ *
+ * @see #setExclusiveContext(boolean)
+ * @see #setExclusiveContext(Thread)
+ */
+ // @Override
+ public final boolean isExclusiveContextEnabled() {
+ stateSync.lock();
+ try {
+ return exclusiveContext;
+ } finally {
+ stateSync.unlock();
+ }
+ }
+
+ /**
+ * Returns the exclusive context thread if {@link #isExclusiveContextEnabled()} and {@link #isStarted()}, otherwise <code>null</code>.
+ * <p>
+ * If exclusive context is enabled via {@link #setExclusiveContext(boolean)}
+ * the {@link #getThread() animator thread} is returned if above conditions are met.
+ * </p>
+ * <p>
+ * If exclusive context is enabled via {@link #setExclusiveContext(Thread)}
+ * the user passed thread is returned if above conditions are met.
+ * </p>
+ * @see #setExclusiveContext(boolean)
+ * @see #setExclusiveContext(Thread)
+ */
+ // @Override
+ public final Thread getExclusiveContextThread() {
+ stateSync.lock();
+ try {
+ return ( isStartedImpl() && exclusiveContext ) ? ( null != userExclusiveContextThread ? userExclusiveContextThread : animThread ) : null ;
+ } finally {
+ stateSync.unlock();
+ }
+ }
+
+ /**
+ * Should be called at {@link #start()} and {@link #stop()}
+ * from within the animator thread.
+ * <p>
+ * At {@link #stop()} an additional {@link #display()} call shall be issued
+ * to allow propagation of releasing the exclusive thread.
+ * </p>
+ */
+ protected final synchronized void setDrawablesExclCtxState(boolean enable) {
+ if(DEBUG) {
+ System.err.println("AnimatorBase.setExclusiveContextImpl exlusive "+exclusiveContext+": Enable "+enable+" for "+this+" - "+Thread.currentThread());
+ // Thread.dumpStack();
+ }
+ final Thread ect = getExclusiveContextThread();
+ for (int i=0; i<drawables.size(); i++) {
+ try {
+ drawables.get(i).setExclusiveContextThread( enable ? ect : null );
+ } catch (RuntimeException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ protected final boolean validateDrawablesExclCtxState(Thread expected) {
+ for (int i=0; i<drawables.size(); i++) {
+ if( expected != drawables.get(i).getExclusiveContextThread() ) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public final Thread getThread() {
+ stateSync.lock();
+ try {
+ return animThread;
+ } finally {
+ stateSync.unlock();
+ }
}
/** Called every frame to cause redrawing of all of the
@@ -143,55 +437,56 @@ public abstract class AnimatorBase implements GLAnimatorControl {
fpsCounter.tickFPS();
}
+ @Override
public final void setUpdateFPSFrames(int frames, PrintStream out) {
fpsCounter.setUpdateFPSFrames(frames, out);
}
+ @Override
public final void resetFPSCounter() {
fpsCounter.resetFPSCounter();
}
+ @Override
public final int getUpdateFPSFrames() {
return fpsCounter.getUpdateFPSFrames();
}
+ @Override
public final long getFPSStartTime() {
return fpsCounter.getFPSStartTime();
}
+ @Override
public final long getLastFPSUpdateTime() {
return fpsCounter.getLastFPSUpdateTime();
}
+ @Override
public final long getLastFPSPeriod() {
return fpsCounter.getLastFPSPeriod();
}
+ @Override
public final float getLastFPS() {
return fpsCounter.getLastFPS();
}
+ @Override
public final int getTotalFPSFrames() {
return fpsCounter.getTotalFPSFrames();
}
+ @Override
public final long getTotalFPSDuration() {
return fpsCounter.getTotalFPSDuration();
}
+ @Override
public final float getTotalFPS() {
return fpsCounter.getTotalFPS();
}
- public final Thread getThread() {
- stateSync.lock();
- try {
- return animThread;
- } finally {
- stateSync.unlock();
- }
- }
-
/** Sets a flag causing this Animator to ignore exceptions produced
while redrawing the drawables. By default this flag is set to
false, causing any exception thrown to halt the Animator. */
@@ -207,7 +502,78 @@ public abstract class AnimatorBase implements GLAnimatorControl {
this.printExceptions = printExceptions;
}
+ protected interface Condition {
+ /**
+ * @return true if branching (continue waiting, action), otherwise false
+ */
+ boolean eval();
+ }
+
+ /**
+ * @param waitCondition method will wait until TO is reached or {@link Condition#eval() waitCondition.eval()} returns <code>false</code>.
+ * @param pollPeriod if <code>0</code>, method will wait until TO is reached or being notified.
+ * if &gt; <code>0</code>, method will wait for the given <code>pollPeriod</code> in milliseconds.
+ * @return <code>true</code> if {@link Condition#eval() waitCondition.eval()} returned <code>false</code>, otherwise <code>false</code>.
+ */
+ protected synchronized boolean finishLifecycleAction(Condition waitCondition, long pollPeriod) {
+ // It's hard to tell whether the thread which changes the lifecycle has
+ // dependencies on the Animator's internal thread. Currently we
+ // use a couple of heuristics to determine whether we should do
+ // the blocking wait().
+ initImpl(false);
+ final boolean blocking = impl.blockUntilDone(animThread);
+ long remaining = blocking ? TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION : 0;
+ if( 0 >= pollPeriod ) {
+ pollPeriod = remaining;
+ }
+ boolean nok = waitCondition.eval();
+ while ( nok && remaining>0 ) {
+ final long t1 = System.currentTimeMillis();
+ if( pollPeriod > remaining ) { pollPeriod = remaining; }
+ notifyAll();
+ try {
+ wait(pollPeriod);
+ } catch (InterruptedException ie) { }
+ remaining -= System.currentTimeMillis() - t1 ;
+ nok = waitCondition.eval();
+ }
+ if(DEBUG || nok) {
+ if( remaining<=0 && nok ) {
+ System.err.println("finishLifecycleAction(" + waitCondition.getClass().getName() + "): ++++++ timeout reached ++++++ " + Thread.currentThread().getName());
+ }
+ stateSync.lock(); // avoid too many lock/unlock ops
+ try {
+ System.err.println("finishLifecycleAction(" + waitCondition.getClass().getName() + "): OK "+(!nok)+
+ "- pollPeriod "+pollPeriod+", blocking "+blocking+
+ ", waited " + (blocking ? ( TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION - remaining ) : 0 ) + "/" + TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION +
+ " - " + Thread.currentThread().getName());
+ System.err.println(" - "+toString());
+ } finally {
+ stateSync.unlock();
+ }
+ if(nok) {
+ Thread.dumpStack();
+ }
+ }
+ return !nok;
+ }
+
+ protected final boolean isStartedImpl() {
+ return animThread != null ;
+ }
+ @Override
+ public boolean isStarted() {
+ stateSync.lock();
+ try {
+ return animThread != null ;
+ } finally {
+ stateSync.unlock();
+ }
+ }
+
public String toString() {
- return getClass().getName()+"[started "+isStarted()+", animating "+isAnimating()+", paused "+isPaused()+", drawable "+drawables.size()+", totals[dt "+getTotalFPSDuration()+", frames "+getTotalFPSFrames()+", fps "+getTotalFPS()+"]]";
+ return getClass().getName()+"[started "+isStarted()+", animating "+isAnimating()+", paused "+isPaused()+", drawable "+drawables.size()+
+ ", totals[dt "+getTotalFPSDuration()+", frames "+getTotalFPSFrames()+", fps "+getTotalFPS()+
+ "], modeBits "+modeBits+", init'ed "+(null!=impl)+", animThread "+getThread()+", exclCtxThread "+exclusiveContext+"("+getExclusiveContextThread()+")]";
}
}
diff --git a/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java b/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java
index 251792110..bfcab23fd 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java
@@ -39,8 +39,11 @@
*/
package com.jogamp.opengl.util;
-import java.util.*;
-import javax.media.opengl.*;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import javax.media.opengl.GLAutoDrawable;
+import javax.media.opengl.GLException;
/**
* An Animator subclass which attempts to achieve a target
@@ -54,10 +57,12 @@ import javax.media.opengl.*;
*/
public class FPSAnimator extends AnimatorBase {
private Timer timer = null;
- private TimerTask task = null;
+ private MainTask task = null;
private int fps;
private boolean scheduleAtFixedRate;
- private volatile boolean shouldRun;
+ private boolean isAnimating; // MainTask feedback
+ private volatile boolean shouldRun; // MainTask trigger
+ private volatile boolean shouldStop; // MainTask trigger
protected String getBaseName(String prefix) {
return "FPS" + prefix + "Animator" ;
@@ -88,6 +93,7 @@ public class FPSAnimator extends AnimatorBase {
value, an initial drawable to animate, and a flag indicating
whether to use fixed-rate scheduling. */
public FPSAnimator(GLAutoDrawable drawable, int fps, boolean scheduleAtFixedRate) {
+ super();
this.fps = fps;
if (drawable != null) {
add(drawable);
@@ -95,133 +101,260 @@ public class FPSAnimator extends AnimatorBase {
this.scheduleAtFixedRate = scheduleAtFixedRate;
}
- public final boolean isStarted() {
- stateSync.lock();
- try {
- return (timer != null);
- } finally {
- stateSync.unlock();
+ /**
+ * @param fps
+ * @throws GLException if the animator has already been started
+ */
+ public final synchronized void setFPS(int fps) throws GLException {
+ if ( isStartedImpl() ) {
+ throw new GLException("Animator already started.");
}
+ this.fps = fps;
}
+ public final int getFPS() { return fps; }
+
+ class MainTask extends TimerTask {
+ private boolean justStarted;
+ private boolean alreadyStopped;
+ private boolean alreadyPaused;
+
+ public MainTask() {
+ }
+
+ public void start(Timer timer) {
+ fpsCounter.resetFPSCounter();
+ shouldRun = true;
+ shouldStop = false;
+
+ justStarted = true;
+ alreadyStopped = false;
+ alreadyPaused = false;
+ final long period = 0 < fps ? (long) (1000.0f / (float) fps) : 1; // 0 -> 1: IllegalArgumentException: Non-positive period
+ if (scheduleAtFixedRate) {
+ timer.scheduleAtFixedRate(this, 0, period);
+ } else {
+ timer.schedule(this, 0, period);
+ }
+ }
+
+ public boolean isActive() { return !alreadyStopped && !alreadyPaused; }
+
+ public String toString() {
+ return "Task[thread "+animThread+", stopped "+alreadyStopped+", paused "+alreadyPaused+" shouldRun "+shouldRun+", shouldStop "+shouldStop+" -- started "+isStartedImpl()+", animating "+isAnimatingImpl()+", paused "+isPausedImpl()+", drawable "+drawables.size()+", drawablesEmpty "+drawablesEmpty+"]";
+ }
+
+ public void run() {
+ if( justStarted ) {
+ justStarted = false;
+ synchronized (FPSAnimator.this) {
+ animThread = Thread.currentThread();
+ if(DEBUG) {
+ System.err.println("FPSAnimator start/resume:" + Thread.currentThread() + ": " + toString());
+ }
+ isAnimating = true;
+ if( drawablesEmpty ) {
+ shouldRun = false; // isAnimating:=false @ pause below
+ } else {
+ shouldRun = true;
+ setDrawablesExclCtxState(exclusiveContext);
+ FPSAnimator.this.notifyAll();
+ }
+ System.err.println("FPSAnimator P1:" + Thread.currentThread() + ": " + toString());
+ }
+ }
+ if( shouldRun ) {
+ display();
+ } else if( shouldStop ) { // STOP
+ System.err.println("FPSAnimator P4: "+alreadyStopped+", "+ Thread.currentThread() + ": " + toString());
+ this.cancel();
+
+ if( !alreadyStopped ) {
+ alreadyStopped = true;
+ if( exclusiveContext && !drawablesEmpty ) {
+ setDrawablesExclCtxState(false);
+ display(); // propagate exclusive change!
+ }
+ synchronized (FPSAnimator.this) {
+ if(DEBUG) {
+ System.err.println("FPSAnimator stop " + Thread.currentThread() + ": " + toString());
+ }
+ animThread = null;
+ isAnimating = false;
+ FPSAnimator.this.notifyAll();
+ }
+ }
+ } else {
+ System.err.println("FPSAnimator P5: "+alreadyPaused+", "+ Thread.currentThread() + ": " + toString());
+ this.cancel();
+
+ if( !alreadyPaused ) { // PAUSE
+ alreadyPaused = true;
+ if( exclusiveContext && !drawablesEmpty ) {
+ setDrawablesExclCtxState(false);
+ display(); // propagate exclusive change!
+ }
+ synchronized (FPSAnimator.this) {
+ if(DEBUG) {
+ System.err.println("FPSAnimator pause " + Thread.currentThread() + ": " + toString());
+ }
+ isAnimating = false;
+ FPSAnimator.this.notifyAll();
+ }
+ }
+ }
+ }
+ }
+ private final boolean isAnimatingImpl() {
+ return animThread != null && isAnimating ;
+ }
public final boolean isAnimating() {
stateSync.lock();
try {
- return (timer != null) && (task != null);
+ return animThread != null && isAnimating ;
} finally {
stateSync.unlock();
}
}
+ private final boolean isPausedImpl() {
+ return animThread != null && ( !shouldRun && !shouldStop ) ;
+ }
public final boolean isPaused() {
stateSync.lock();
try {
- return (timer != null) && (task == null);
+ return animThread != null && ( !shouldRun && !shouldStop ) ;
} finally {
stateSync.unlock();
}
}
- private void startTask() {
- if(null != task) {
- return;
- }
- final long period = (long) (1000.0f / (float) fps);
- task = new TimerTask() {
- public void run() {
- if(FPSAnimator.this.shouldRun) {
- FPSAnimator.this.animThread = Thread.currentThread();
- // display impl. uses synchronized block on the animator instance
- display();
- }
- }
- };
-
- fpsCounter.resetFPSCounter();
- shouldRun = true;
-
- if (scheduleAtFixedRate) {
- timer.scheduleAtFixedRate(task, 0, period);
- } else {
- timer.schedule(task, 0, period);
- }
- }
-
- public synchronized boolean start() {
- if (timer != null) {
+ static int timerNo = 0;
+
+ public synchronized boolean start() {
+ if ( null != timer || null != task || isStartedImpl() ) {
return false;
}
- stateSync.lock();
- try {
- timer = new Timer();
- startTask();
- } finally {
- stateSync.unlock();
+ timer = new Timer( Thread.currentThread().getName()+"-"+baseName+"-Timer"+(timerNo++) );
+ task = new MainTask();
+ if(DEBUG) {
+ System.err.println("FPSAnimator.start() START: "+task+", "+ Thread.currentThread() + ": " + toString());
}
- return true;
+ task.start(timer);
+
+ final boolean res = finishLifecycleAction( drawablesEmpty ? waitForStartedEmptyCondition : waitForStartedAddedCondition,
+ POLLP_WAIT_FOR_FINISH_LIFECYCLE_ACTION);
+ if(DEBUG) {
+ System.err.println("FPSAnimator.start() END: "+task+", "+ Thread.currentThread() + ": " + toString());
+ }
+ if( drawablesEmpty ) {
+ task.cancel();
+ task = null;
+ }
+ return res;
}
+ private final Condition waitForStartedAddedCondition = new Condition() {
+ public boolean eval() {
+ return !isStartedImpl() || !isAnimating ;
+ } };
+ private final Condition waitForStartedEmptyCondition = new Condition() {
+ public boolean eval() {
+ return !isStartedImpl() || isAnimating ;
+ } };
/** Stops this FPSAnimator. Due to the implementation of the
FPSAnimator it is not guaranteed that the FPSAnimator will be
completely stopped by the time this method returns. */
public synchronized boolean stop() {
- if (timer == null) {
+ if ( null == timer || !isStartedImpl() ) {
return false;
+ }
+ if(DEBUG) {
+ System.err.println("FPSAnimator.stop() START: "+task+", "+ Thread.currentThread() + ": " + toString());
}
- stateSync.lock();
- try {
+ final boolean res;
+ if( null == task ) {
+ // start/resume case w/ drawablesEmpty
+ res = true;
+ } else {
shouldRun = false;
- if(null != task) {
- task.cancel();
- task = null;
- }
- if(null != timer) {
- timer.cancel();
- timer = null;
- }
- animThread = null;
- try {
- final long periodx2 = 2L * (long) (1000.0f / (float) fps);
- Thread.sleep(periodx2 > 20L ? periodx2 : 20L); // max(2 x timer period, ~ 1/60), since we can't ctrl stopped threads
- } catch (InterruptedException e) { }
- } finally {
- stateSync.unlock();
+ shouldStop = true;
+ res = finishLifecycleAction(waitForStoppedCondition, POLLP_WAIT_FOR_FINISH_LIFECYCLE_ACTION);
+ }
+
+ if(DEBUG) {
+ System.err.println("FPSAnimator.stop() END: "+task+", "+ Thread.currentThread() + ": " + toString());
+ }
+ if(null != task) {
+ task.cancel();
+ task = null;
}
- return true;
+ if(null != timer) {
+ timer.cancel();
+ timer = null;
+ }
+ animThread = null;
+ return res;
}
+ private final Condition waitForStoppedCondition = new Condition() {
+ public boolean eval() {
+ return isStartedImpl();
+ } };
public synchronized boolean pause() {
- if (timer == null) {
+ if ( !isStartedImpl() || ( null != task && isPausedImpl() ) ) {
return false;
}
- stateSync.lock();
- try {
+ if(DEBUG) {
+ System.err.println("FPSAnimator.pause() START: "+task+", "+ Thread.currentThread() + ": " + toString());
+ }
+ final boolean res;
+ if( null == task ) {
+ // start/resume case w/ drawablesEmpty
+ res = true;
+ } else {
shouldRun = false;
- if(null != task) {
- task.cancel();
- task = null;
- }
- animThread = null;
- try {
- final long periodx2 = 2L * (long) (1000.0f / (float) fps);
- Thread.sleep(periodx2 > 20L ? periodx2 : 20L); // max(2 x timer period, ~ 1/60), since we can't ctrl stopped threads
- } catch (InterruptedException e) { }
- } finally {
- stateSync.unlock();
+ res = finishLifecycleAction(waitForPausedCondition, POLLP_WAIT_FOR_FINISH_LIFECYCLE_ACTION);
+ }
+
+ if(DEBUG) {
+ System.err.println("FPSAnimator.pause() END: "+task+", "+ Thread.currentThread() + ": " + toString());
+ }
+ if(null != task) {
+ task.cancel();
+ task = null;
}
- return true;
+ return res;
}
+ private final Condition waitForPausedCondition = new Condition() {
+ public boolean eval() {
+ // end waiting if stopped as well
+ return isAnimating && isStartedImpl();
+ } };
public synchronized boolean resume() {
- if (timer == null) {
+ if ( null != task || !isStartedImpl() || !isPausedImpl() ) {
return false;
}
- stateSync.lock();
- try {
- startTask();
- } finally {
- stateSync.unlock();
+ if(DEBUG) {
+ System.err.println("FPSAnimator.resume() START: "+ Thread.currentThread() + ": " + toString());
+ }
+ final boolean res;
+ if( drawablesEmpty ) {
+ res = true;
+ } else {
+ task = new MainTask();
+ task.start(timer);
+ res = finishLifecycleAction(waitForResumeCondition, POLLP_WAIT_FOR_FINISH_LIFECYCLE_ACTION);
+ }
+ if(DEBUG) {
+ System.err.println("FPSAnimator.resume() END: "+task+", "+ Thread.currentThread() + ": " + toString());
}
- return true;
+ return res;
}
+ private final Condition waitForResumeCondition = new Condition() {
+ public boolean eval() {
+ // end waiting if stopped as well
+ return !drawablesEmpty && !isAnimating && isStartedImpl();
+ } };
}
diff --git a/src/jogl/classes/javax/media/opengl/GLAnimatorControl.java b/src/jogl/classes/javax/media/opengl/GLAnimatorControl.java
index 83e9e22c4..3052b924e 100644
--- a/src/jogl/classes/javax/media/opengl/GLAnimatorControl.java
+++ b/src/jogl/classes/javax/media/opengl/GLAnimatorControl.java
@@ -117,7 +117,7 @@ public interface GLAnimatorControl extends FPSCounter {
* or in some cases from an implementation-internal thread like the
* AWT event queue thread.
*
- * @return false if if not started or already paused, otherwise true
+ * @return false if not started, already paused or failed to pause, otherwise true
*
* @see #resume()
* @see #isAnimating()
@@ -134,7 +134,7 @@ public interface GLAnimatorControl extends FPSCounter {
* <P>
* If resumed, all counters (time, frames, ..) are reset to zero.
*
- * @return false if if not started or not paused, otherwise true
+ * @return false if not started, not paused or unable to resume, otherwise true
*
* @see #pause()
* @see #isAnimating()
@@ -142,13 +142,24 @@ public interface GLAnimatorControl extends FPSCounter {
boolean resume();
/**
+ * Adds a drawable to this animator's list of rendering drawables.<br>
+ * This allows the animator thread to become active, i.e. {@link #isAnimating()}==true,
+ * in case the first drawable is added and {@link #isStarted()} and not {@link #isPaused()}.<br>
+ *
+ * @param drawable the drawable to be added
+ * @throws IllegalArgumentException if drawable was already added to this animator
+ */
+ void add(GLAutoDrawable drawable);
+
+ /**
* Removes a drawable from the animator's list of rendering drawables.<br>
* This method should get called in case a drawable becomes invalid,
* and will not be recovered.<br>
- * This allows the animator thread to become idle in case the last drawable
- * has reached it's end of life.<br>
+ * This allows the animator thread to become idle, i.e. {@link #isAnimating()}==false,
+ * in case the last drawable has reached it's end of life.<br>
*
- * @param drawable the to be removed drawable
+ * @param drawable the drawable to be removed
+ * @throws IllegalArgumentException if drawable was not added to this animator
*/
void remove(GLAutoDrawable drawable);
}
diff --git a/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java b/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java
index 0f487f463..a7db3f3fd 100644
--- a/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java
+++ b/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java
@@ -311,13 +311,13 @@ public interface GLAutoDrawable extends GLDrawable {
public GLEventListener removeGLEventListener(GLEventListener listener);
/**
- * <p>
* Registers the usage of an animator, an {@link javax.media.opengl.GLAnimatorControl} implementation.
- * The animator will be queried whether it's animating, ie periodically issuing {@link #display()} calls or not.</p><br>
+ * The animator will be queried whether it's animating, ie periodically issuing {@link #display()} calls or not.
* <p>
* This method shall be called by an animator implementation only,<br>
* e.g. {@link com.jogamp.opengl.util.Animator#add(javax.media.opengl.GLAutoDrawable)}, passing it's control implementation,<br>
- * and {@link com.jogamp.opengl.util.Animator#remove(javax.media.opengl.GLAutoDrawable)}, passing <code>null</code>.</p><br>
+ * and {@link com.jogamp.opengl.util.Animator#remove(javax.media.opengl.GLAutoDrawable)}, passing <code>null</code>.
+ * </p>
* <p>
* Impacts {@link #display()} and {@link #invoke(boolean, GLRunnable)} semantics.</p><br>
*
@@ -341,6 +341,45 @@ public interface GLAutoDrawable extends GLDrawable {
public GLAnimatorControl getAnimator();
/**
+ * Dedicates this instance's {@link GLContext} to the given thread.<br/>
+ * The thread will exclusively claim the {@link GLContext} via {@link #display()} and not release it
+ * until {@link #destroy()} or <code>setExclusiveContextThread(null)</code> has been called.
+ * <p>
+ * Default non-exclusive behavior is <i>requested</i> via <code>setExclusiveContextThread(null)</code>,
+ * which will cause the next call of {@link #display()} on the exclusive thread to
+ * release the {@link GLContext}. Only after it's async release, {@link #getExclusiveContextThread()}
+ * will return <code>null</code>.
+ * </p>
+ * <p>
+ * To release a previous made exclusive thread, a user issues <code>setExclusiveContextThread(null)</code>
+ * and may poll {@link #getExclusiveContextThread()} until it returns <code>null</code>,
+ * <i>while</i> the exclusive thread is still running.
+ * </p>
+ * <p>
+ * Note: Setting a new exclusive thread without properly releasing a previous one
+ * will throw an GLException.
+ * </p>
+ * <p>
+ * Note: Utilizing this feature w/ AWT could lead to an AWT-EDT deadlock, depending on the AWT implementation.
+ * Hence it is advised not to use it with native AWT GLAutoDrawable like GLCanvas.
+ * </p>
+ * <p>
+ * One scenario could be to dedicate the context to the {@link GLAnimatorControl#getThread() animator thread}
+ * and spare redundant context switches, see {@link com.jogamp.opengl.util.AnimatorBase#setExclusiveContext(boolean)}.
+ * </p>
+ * @param t the exclusive thread to claim the context, or <code>null</code> for default operation.
+ * @return previous exclusive context thread
+ * @throws GLException If an exclusive thread is still active but a new one is attempted to be set
+ * @see com.jogamp.opengl.util.AnimatorBase#setExclusiveContext(boolean)
+ */
+ public Thread setExclusiveContextThread(Thread t) throws GLException;
+
+ /**
+ * @see #setExclusiveContextThread(Thread)
+ */
+ public Thread getExclusiveContextThread();
+
+ /**
* Enqueues a one-shot {@link GLRunnable},
* which will be executed within the next {@link #display()} call
* after all registered {@link GLEventListener}s
diff --git a/src/jogl/classes/javax/media/opengl/GLContext.java b/src/jogl/classes/javax/media/opengl/GLContext.java
index 235003c38..461d481a8 100644
--- a/src/jogl/classes/javax/media/opengl/GLContext.java
+++ b/src/jogl/classes/javax/media/opengl/GLContext.java
@@ -276,6 +276,9 @@ public abstract class GLContext {
/**
* Makes this GLContext current on the calling thread.
* <p>
+ * Recursive call to {@link #makeCurrent()} and hence {@link #release()} are supported.
+ * </p>
+ * <p>
* There are two return values that indicate success and one that
* indicates failure.
* </p>
@@ -288,7 +291,7 @@ public abstract class GLContext {
* </p>
* <p>
* A return value of {@link #CONTEXT_CURRENT} indicates that the context has
- * been made currrent, with its previous state restored.
+ * been made current, with its previous state restored.
* </p>
* <p>
* If the context could not be made current (for example, because
@@ -320,6 +323,9 @@ public abstract class GLContext {
/**
* Releases control of this GLContext from the current thread.
* <p>
+ * Recursive call to {@link #release()} and hence {@link #makeCurrent()} are supported.
+ * </p>
+ * <p>
* The drawable's surface is being unlocked at exit,
* assumed to be locked by {@link #makeCurrent()}.
* </p>
diff --git a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java
index 2f7fef9be..2de86b545 100644
--- a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java
+++ b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java
@@ -742,6 +742,16 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
}
@Override
+ public final Thread setExclusiveContextThread(Thread t) throws GLException {
+ return helper.setExclusiveContextThread(t, context);
+ }
+
+ @Override
+ public final Thread getExclusiveContextThread() {
+ return helper.getExclusiveContextThread();
+ }
+
+ @Override
public boolean invoke(boolean wait, GLRunnable glRunnable) {
return helper.invoke(this, wait, glRunnable);
}
diff --git a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java
index 23dedaa66..664edb996 100644
--- a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java
+++ b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java
@@ -474,6 +474,16 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
}
@Override
+ public final Thread setExclusiveContextThread(Thread t) throws GLException {
+ return helper.setExclusiveContextThread(t, getContext());
+ }
+
+ @Override
+ public final Thread getExclusiveContextThread() {
+ return helper.getExclusiveContextThread();
+ }
+
+ @Override
public boolean invoke(boolean wait, GLRunnable glRunnable) {
return helper.invoke(this, wait, glRunnable);
}
diff --git a/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java b/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java
index cbb7cd699..eadd59559 100644
--- a/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java
+++ b/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java
@@ -50,7 +50,6 @@ import javax.media.opengl.GLRunnable;
import com.jogamp.common.util.locks.RecursiveLock;
import com.jogamp.opengl.GLAutoDrawableDelegate;
-import com.jogamp.opengl.util.Animator;
/**
@@ -407,6 +406,16 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, FPSCounter {
}
@Override
+ public final Thread setExclusiveContextThread(Thread t) throws GLException {
+ return helper.setExclusiveContextThread(t, context);
+ }
+
+ @Override
+ public final Thread getExclusiveContextThread() {
+ return helper.getExclusiveContextThread();
+ }
+
+ @Override
public final boolean invoke(boolean wait, GLRunnable glRunnable) {
return helper.invoke(this, wait, glRunnable);
}
@@ -532,25 +541,6 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, FPSCounter {
return null != _drawable ? _drawable.getHeight() : 0;
}
- /**
- * @param t the thread for which context release shall be skipped, usually the animation thread,
- * ie. {@link Animator#getThread()}.
- * @deprecated This is an experimental feature,
- * intended for measuring performance in regards to GL context switch.
- */
- @Deprecated
- public void setSkipContextReleaseThread(Thread t) {
- helper.setSkipContextReleaseThread(t);
- }
-
- /**
- * @deprecated see {@link #setSkipContextReleaseThread(Thread)}
- */
- @Deprecated
- public Thread getSkipContextReleaseThread() {
- return helper.getSkipContextReleaseThread();
- }
-
@Override
public final GLCapabilitiesImmutable getChosenGLCapabilities() {
final GLDrawable _drawable = drawable;
diff --git a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java
index dc5d50cf2..f8c58ee34 100644
--- a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java
+++ b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java
@@ -58,8 +58,6 @@ import javax.media.opengl.GLException;
import javax.media.opengl.GLFBODrawable;
import javax.media.opengl.GLRunnable;
-import com.jogamp.opengl.util.Animator;
-
/** Encapsulates the implementation of most of the GLAutoDrawable's
methods to be able to share it between GLCanvas and GLJPanel. */
public class GLDrawableHelper {
@@ -73,7 +71,9 @@ public class GLDrawableHelper {
private final Object glRunnablesLock = new Object();
private volatile ArrayList<GLRunnableTask> glRunnables = new ArrayList<GLRunnableTask>();
private boolean autoSwapBufferMode;
- private Thread skipContextReleaseThread;
+ private volatile Thread exclusiveContextThread;
+ /** -1 release, 0 nop, 1 claim */
+ private volatile int exclusiveContextSwitch;
private GLAnimatorControl animatorCtrl;
private static Runnable nop = new Runnable() { public void run() {} };
@@ -87,7 +87,8 @@ public class GLDrawableHelper {
listenersToBeInit.clear();
}
autoSwapBufferMode = true;
- skipContextReleaseThread = null;
+ exclusiveContextThread = null;
+ exclusiveContextSwitch = 0;
synchronized(glRunnablesLock) {
glRunnables.clear();
}
@@ -113,6 +114,23 @@ public class GLDrawableHelper {
}
/**
+ * Since GLContext's {@link GLContext#makeCurrent()} and {@link GLContext#release()}
+ * is recursive, a call to {@link GLContext#release()} may not natively release the context.
+ * <p>
+ * This methods continues calling {@link GLContext#release()} until the context has been natively released.
+ * </p>
+ * @param ctx
+ */
+ public static final void forceNativeRelease(GLContext ctx) {
+ do {
+ ctx.release();
+ if (DEBUG) {
+ System.err.println("GLDrawableHelper.forceNativeRelease() -- currentThread "+Thread.currentThread()+" -> "+GLContext.getCurrent());
+ }
+ } while( ctx == GLContext.getCurrent() );
+ }
+
+ /**
* 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>
@@ -767,23 +785,72 @@ public class GLDrawableHelper {
return autoSwapBufferMode;
}
+ private final String getExclusiveContextSwitchString() {
+ return 0 == exclusiveContextSwitch ? "nop" : ( 0 > exclusiveContextSwitch ? "released" : "claimed" ) ;
+ }
+
/**
- * @param t the thread for which context release shall be skipped, usually the animation thread,
- * ie. {@link Animator#getThread()}.
- * @deprecated this is an experimental feature,
- * intended for measuring performance in regards to GL context switch
- * and only being used if {@link #PERF_STATS} is enabled
- * by defining property <code>jogl.debug.GLDrawable.PerfStats</code>.
+ * Dedicates this instance's {@link GLContext} to the given thread.<br/>
+ * The thread will exclusively claim the {@link GLContext} via {@link #display()} and not release it
+ * until {@link #destroy()} or <code>setExclusiveContextThread(null)</code> has been called.
+ * <p>
+ * Default non-exclusive behavior is <i>requested</i> via <code>setExclusiveContextThread(null)</code>,
+ * which will cause the next call of {@link #display()} on the exclusive thread to
+ * release the {@link GLContext}. Only after it's async release, {@link #getExclusiveContextThread()}
+ * will return <code>null</code>.
+ * </p>
+ * <p>
+ * To release a previous made exclusive thread, a user issues <code>setExclusiveContextThread(null)</code>
+ * and may poll {@link #getExclusiveContextThread()} until it returns <code>null</code>,
+ * <i>while</i> the exclusive thread is still running.
+ * </p>
+ * <p>
+ * Note: Setting a new exclusive thread without properly releasing a previous one
+ * will throw an GLException.
+ * </p>
+ * <p>
+ * One scenario could be to dedicate the context to the {@link com.jogamp.opengl.util.AnimatorBase#getThread() animator thread}
+ * and spare redundant context switches.
+ * </p>
+ * @param t the exclusive thread to claim the context, or <code>null</code> for default operation.
+ * @return previous exclusive context thread
+ * @throws GLException If an exclusive thread is still active but a new one is attempted to be set
*/
- public final void setSkipContextReleaseThread(Thread t) {
- skipContextReleaseThread = t;
+ public final Thread setExclusiveContextThread(Thread t, GLContext context) throws GLException {
+ if (DEBUG) {
+ System.err.println("GLDrawableHelper.setExclusiveContextThread(): START switch "+getExclusiveContextSwitchString()+", thread "+exclusiveContextThread+" -> "+t+" -- currentThread "+Thread.currentThread());
+ }
+ final Thread oldExclusiveContextThread = exclusiveContextThread;
+ if( exclusiveContextThread == t ) {
+ exclusiveContextSwitch = 0; // keep
+ } else if( null == t ) {
+ exclusiveContextSwitch = -1; // release
+ } else {
+ exclusiveContextSwitch = 1; // claim
+ if( null != exclusiveContextThread ) {
+ throw new GLException("Release current exclusive Context Thread "+exclusiveContextThread+" first");
+ }
+ if( null != context && GLContext.getCurrent() == context ) {
+ try {
+ forceNativeRelease(context);
+ } catch (Throwable ex) {
+ ex.printStackTrace();
+ throw new GLException(ex);
+ }
+ }
+ exclusiveContextThread = t;
+ }
+ if (DEBUG) {
+ System.err.println("GLDrawableHelper.setExclusiveContextThread(): END switch "+getExclusiveContextSwitchString()+", thread "+exclusiveContextThread+" -- currentThread "+Thread.currentThread());
+ }
+ return oldExclusiveContextThread;
}
-
+
/**
- * @deprecated see {@link #setSkipContextReleaseThread(Thread)}
+ * @see #setExclusiveContextThread(Thread, GLContext)
*/
- public final Thread getSkipContextReleaseThread() {
- return skipContextReleaseThread;
+ public final Thread getExclusiveContextThread() {
+ return exclusiveContextThread;
}
private static final ThreadLocal<Runnable> perThreadInitAction = new ThreadLocal<Runnable>();
@@ -828,7 +895,10 @@ public class GLDrawableHelper {
* {@link #disposeAllGLEventListener(GLAutoDrawable, boolean) disposeAllGLEventListener(autoDrawable, false)}
* with the context made current.
* <p>
- * If <code>destroyContext</code> is <code>true</code> the context is destroyed in the end while holding the lock.<br/>
+ * If <code>destroyContext</code> is <code>true</code> the context is destroyed in the end while holding the lock.
+ * </p>
+ * <p>
+ * If <code>destroyContext</code> is <code>false</code> the context is natively released, i.e. released as often as locked before.
* </p>
* @param autoDrawable
* @param context
@@ -842,14 +912,15 @@ public class GLDrawableHelper {
Runnable lastInitAction = null;
if (lastContext != null) {
if (lastContext == context) {
- lastContext = null; // utilize recursive locking
+ lastContext = null;
} else {
+ // utilize recursive locking
lastInitAction = perThreadInitAction.get();
lastContext.release();
}
}
- int res = GLContext.CONTEXT_NOT_CURRENT;
-
+
+ int res;
try {
res = context.makeCurrent();
if (GLContext.CONTEXT_NOT_CURRENT != res) {
@@ -865,7 +936,7 @@ public class GLDrawableHelper {
if(destroyContext) {
context.destroy();
} else {
- context.release();
+ forceNativeRelease(context);
}
flushGLRunnables();
} catch (Exception e) {
@@ -880,136 +951,204 @@ public class GLDrawableHelper {
}
}
}
-
+
private final void invokeGLImpl(final GLDrawable drawable,
- final GLContext context,
- final Runnable runnable,
- final Runnable initAction) {
- // Support for recursive makeCurrent() calls as well as calling
- // other drawables' display() methods from within another one's
- GLContext lastContext = GLContext.getCurrent();
- Runnable lastInitAction = null;
- if (lastContext != null) {
- if (lastContext == context) {
- lastContext = null; // utilize recursive locking
- } else {
- lastInitAction = perThreadInitAction.get();
- lastContext.release();
- }
- }
- int res = GLContext.CONTEXT_NOT_CURRENT;
-
- try {
- res = context.makeCurrent();
- if (GLContext.CONTEXT_NOT_CURRENT != res) {
- try {
- perThreadInitAction.set(initAction);
- if (GLContext.CONTEXT_CURRENT_NEW == res) {
- if (DEBUG) {
- System.err.println("GLDrawableHelper " + this + ".invokeGL(): Running initAction");
- }
- initAction.run();
- }
- runnable.run();
- if ( autoSwapBufferMode ) {
- drawable.swapBuffers();
- }
- } finally {
- try {
- context.release();
- } catch (Exception e) {
- System.err.println("Catched: "+e.getMessage());
- e.printStackTrace();
- }
- }
- }
- } finally {
- if (lastContext != null) {
- final int res2 = lastContext.makeCurrent();
- if (null != lastInitAction && res2 == GLContext.CONTEXT_CURRENT_NEW) {
- lastInitAction.run();
- }
- }
- }
- }
-
- private final void invokeGLImplStats(final GLDrawable drawable,
- final GLContext context,
- final Runnable runnable,
- final Runnable initAction) {
- final Thread currentThread = Thread.currentThread();
-
- // Support for recursive makeCurrent() calls as well as calling
- // other drawables' display() methods from within another one's
- int res = GLContext.CONTEXT_NOT_CURRENT;
- GLContext lastContext = GLContext.getCurrent();
- Runnable lastInitAction = null;
- if (lastContext != null) {
- if (lastContext == context) {
- if( currentThread == skipContextReleaseThread ) {
- res = GLContext.CONTEXT_CURRENT;
- } // else: utilize recursive locking
- lastContext = null;
- } else {
- lastInitAction = perThreadInitAction.get();
- lastContext.release();
- }
- }
-
- long t0 = System.currentTimeMillis();
- long tdA = 0; // makeCurrent
- long tdR = 0; // render time
- long tdS = 0; // swapBuffers
- long tdX = 0; // release
- boolean ctxClaimed = false;
- boolean ctxReleased = false;
- boolean ctxDestroyed = false;
- try {
- if (res == GLContext.CONTEXT_NOT_CURRENT) {
- res = context.makeCurrent();
- ctxClaimed = true;
+ final GLContext context,
+ final Runnable runnable,
+ final Runnable initAction) {
+ final Thread currentThread = Thread.currentThread();
+
+ // Exclusive Cases:
+ // 1: lock - unlock : default
+ // 2: lock - - : exclusive, not locked yet
+ // 3: - - - : exclusive, already locked
+ // 4: - - unlock : ex-exclusive, already locked
+ final boolean _isExclusiveThread, _releaseExclusiveThread;
+ if( null != exclusiveContextThread) {
+ if( currentThread == exclusiveContextThread ) {
+ _releaseExclusiveThread = 0 > exclusiveContextSwitch;
+ _isExclusiveThread = !_releaseExclusiveThread;
+ exclusiveContextSwitch = 0;
+ } else {
+ // Exclusive thread usage, but on other thread
+ return;
+ }
+ } else {
+ _releaseExclusiveThread = false;
+ _isExclusiveThread = false;
}
- if (res != GLContext.CONTEXT_NOT_CURRENT) {
- perThreadInitAction.set(initAction);
- if (res == GLContext.CONTEXT_CURRENT_NEW) {
- if (DEBUG) {
- System.err.println("GLDrawableHelper " + this + ".invokeGL(): Running initAction");
+
+ // Support for recursive makeCurrent() calls as well as calling
+ // other drawables' display() methods from within another one's
+ int res = GLContext.CONTEXT_NOT_CURRENT;
+ GLContext lastContext = GLContext.getCurrent();
+ Runnable lastInitAction = null;
+ if (lastContext != null) {
+ if (lastContext == context) {
+ res = GLContext.CONTEXT_CURRENT;
+ lastContext = null;
+ } else {
+ // utilize recursive locking
+ lastInitAction = perThreadInitAction.get();
+ lastContext.release();
}
- initAction.run();
- }
- tdR = System.currentTimeMillis();
- tdA = tdR - t0; // makeCurrent
- runnable.run();
- tdS = System.currentTimeMillis();
- tdR = tdS - tdR; // render time
- if (autoSwapBufferMode) {
- drawable.swapBuffers();
- tdX = System.currentTimeMillis();
- tdS = tdX - tdS; // swapBuffers
- }
}
- } finally {
+
try {
- if( res != GLContext.CONTEXT_NOT_CURRENT &&
- (null == skipContextReleaseThread || currentThread != skipContextReleaseThread) ) {
- context.release();
- ctxReleased = true;
+ final boolean releaseContext;
+ if( GLContext.CONTEXT_NOT_CURRENT == res ) {
+ res = context.makeCurrent();
+ releaseContext = !_isExclusiveThread;
+ } else {
+ releaseContext = _releaseExclusiveThread;
}
- } catch (Exception e) {
- System.err.println("Catched: "+e.getMessage());
- e.printStackTrace();
+ if (GLContext.CONTEXT_NOT_CURRENT != res) {
+ try {
+ perThreadInitAction.set(initAction);
+ if (GLContext.CONTEXT_CURRENT_NEW == res) {
+ if (DEBUG) {
+ System.err.println("GLDrawableHelper " + this + ".invokeGL(): Running initAction");
+ }
+ initAction.run();
+ }
+ runnable.run();
+ if ( autoSwapBufferMode ) {
+ drawable.swapBuffers();
+ }
+ } finally {
+ if( _releaseExclusiveThread ) {
+ exclusiveContextThread = null;
+ if (DEBUG) {
+ System.err.println("GLDrawableHelper.invokeGL() - Release ExclusiveContextThread -- currentThread "+Thread.currentThread());
+ }
+ }
+ if( releaseContext ) {
+ try {
+ context.release();
+ } catch (Exception e) {
+ System.err.println("Catched: "+e.getMessage());
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ } finally {
+ if (lastContext != null) {
+ final int res2 = lastContext.makeCurrent();
+ if (null != lastInitAction && res2 == GLContext.CONTEXT_CURRENT_NEW) {
+ lastInitAction.run();
+ }
+ }
+ }
+ }
+
+ private final void invokeGLImplStats(final GLDrawable drawable,
+ final GLContext context,
+ final Runnable runnable,
+ final Runnable initAction) {
+ final Thread currentThread = Thread.currentThread();
+
+ // Exclusive Cases:
+ // 1: lock - unlock : default
+ // 2: lock - - : exclusive, not locked yet
+ // 3: - - - : exclusive, already locked
+ // 4: - - unlock : ex-exclusive, already locked
+ final boolean _isExclusiveThread, _releaseExclusiveThread;
+ if( null != exclusiveContextThread) {
+ if( currentThread == exclusiveContextThread ) {
+ _releaseExclusiveThread = 0 > exclusiveContextSwitch;
+ _isExclusiveThread = !_releaseExclusiveThread;
+ } else {
+ // Exclusive thread usage, but on other thread
+ return;
+ }
+ } else {
+ _releaseExclusiveThread = false;
+ _isExclusiveThread = false;
}
- tdX = System.currentTimeMillis() - tdX; // release / destroy
+ // Support for recursive makeCurrent() calls as well as calling
+ // other drawables' display() methods from within another one's
+ int res = GLContext.CONTEXT_NOT_CURRENT;
+ GLContext lastContext = GLContext.getCurrent();
+ Runnable lastInitAction = null;
if (lastContext != null) {
- final int res2 = lastContext.makeCurrent();
- if (null != lastInitAction && res2 == GLContext.CONTEXT_CURRENT_NEW) {
- lastInitAction.run();
- }
+ if (lastContext == context) {
+ res = GLContext.CONTEXT_CURRENT;
+ lastContext = null;
+ } else {
+ // utilize recursive locking
+ lastInitAction = perThreadInitAction.get();
+ lastContext.release();
+ }
}
- }
- long td = System.currentTimeMillis() - t0;
- System.err.println("td0 "+td+"ms, fps "+(1.0/(td/1000.0))+", td-makeCurrent: "+tdA+"ms, td-render "+tdR+"ms, td-swap "+tdS+"ms, td-release "+tdX+"ms, ctx claimed: "+ctxClaimed+", ctx release: "+ctxReleased+", ctx destroyed "+ctxDestroyed);
+
+ long t0 = System.currentTimeMillis();
+ long tdA = 0; // makeCurrent
+ long tdR = 0; // render time
+ long tdS = 0; // swapBuffers
+ long tdX = 0; // release
+ boolean ctxClaimed = false;
+ boolean ctxReleased = false;
+ boolean ctxDestroyed = false;
+ try {
+ final boolean releaseContext;
+ if( GLContext.CONTEXT_NOT_CURRENT == res ) {
+ res = context.makeCurrent();
+ releaseContext = !_isExclusiveThread;
+ ctxClaimed = true;
+ } else {
+ releaseContext = _releaseExclusiveThread;
+ }
+ if (GLContext.CONTEXT_NOT_CURRENT != res) {
+ try {
+ perThreadInitAction.set(initAction);
+ if (GLContext.CONTEXT_CURRENT_NEW == res) {
+ if (DEBUG) {
+ System.err.println("GLDrawableHelper " + this + ".invokeGL(): Running initAction");
+ }
+ initAction.run();
+ }
+ tdR = System.currentTimeMillis();
+ tdA = tdR - t0; // makeCurrent
+ runnable.run();
+ tdS = System.currentTimeMillis();
+ tdR = tdS - tdR; // render time
+ if ( autoSwapBufferMode ) {
+ drawable.swapBuffers();
+ tdX = System.currentTimeMillis();
+ tdS = tdX - tdS; // swapBuffers
+ }
+ } finally {
+ if( _releaseExclusiveThread ) {
+ exclusiveContextSwitch = 0;
+ exclusiveContextThread = null;
+ if (DEBUG) {
+ System.err.println("GLDrawableHelper.invokeGL() - Release ExclusiveContextThread -- currentThread "+Thread.currentThread());
+ }
+ }
+ if( releaseContext ) {
+ try {
+ context.release();
+ ctxReleased = true;
+ } catch (Exception e) {
+ System.err.println("Catched: "+e.getMessage());
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ } finally {
+ tdX = System.currentTimeMillis() - tdX; // release / destroy
+ if (lastContext != null) {
+ final int res2 = lastContext.makeCurrent();
+ if (null != lastInitAction && res2 == GLContext.CONTEXT_CURRENT_NEW) {
+ lastInitAction.run();
+ }
+ }
+ }
+ long td = System.currentTimeMillis() - t0;
+ System.err.println("td0 "+td+"ms, fps "+(1.0/(td/1000.0))+", td-makeCurrent: "+tdA+"ms, td-render "+tdR+"ms, td-swap "+tdS+"ms, td-release "+tdX+"ms, ctx claimed: "+ctxClaimed+", ctx release: "+ctxReleased+", ctx destroyed "+ctxDestroyed);
}
}
diff --git a/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtAppletBase.java b/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtAppletBase.java
index 082c01c23..07004503e 100755
--- a/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtAppletBase.java
+++ b/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtAppletBase.java
@@ -208,7 +208,10 @@ public class JOGLNewtAppletBase implements KeyListener, GLEventListener {
glWindow.setUpdateFPSFrames(FPSCounter.DEFAULT_FRAMES_PER_INTERVAL, System.err);
// glAnimator = new FPSAnimator(canvas, 60);
- glAnimator = new Animator(tg, glWindow);
+ glAnimator = new Animator();
+ glAnimator.setModeBits(false, Animator.MODE_EXPECT_AWT_RENDERING_THREAD); // No AWT thread involved!
+ glAnimator.setThreadGroup(tg);
+ glAnimator.add(glWindow);
glAnimator.setUpdateFPSFrames(FPSCounter.DEFAULT_FRAMES_PER_INTERVAL, null);
} catch (Throwable t) {
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/ExclusiveContextBase00.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/ExclusiveContextBase00.java
new file mode 100644
index 000000000..c6eb3a103
--- /dev/null
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/ExclusiveContextBase00.java
@@ -0,0 +1,420 @@
+/**
+ * Copyright 2013 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+
+package com.jogamp.opengl.test.junit.jogl.acore;
+
+import com.jogamp.newt.NewtFactory;
+import com.jogamp.newt.Window;
+import com.jogamp.opengl.test.junit.util.AWTRobotUtil;
+import com.jogamp.opengl.test.junit.util.UITestCase;
+
+import com.jogamp.opengl.util.Animator;
+import com.jogamp.opengl.util.AnimatorBase;
+
+import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2;
+
+import javax.media.nativewindow.Capabilities;
+import javax.media.nativewindow.util.InsetsImmutable;
+
+import javax.media.opengl.GLAutoDrawable;
+import javax.media.opengl.GLCapabilities;
+import javax.media.opengl.GLCapabilitiesImmutable;
+import javax.media.opengl.GLProfile;
+
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.AfterClass;
+import org.junit.Test;
+
+/**
+ * ExclusiveContextThread base implementation to test correctness of the ExclusiveContext feature _and_ AnimatorBase.
+ */
+public abstract class ExclusiveContextBase00 extends UITestCase {
+ static boolean testExclusiveWithAWT = false;
+ static final int durationParts = 9;
+ static long duration = 320 * durationParts; // ms ~ 20 frames
+
+ static boolean showFPS = false;
+ static int showFPSRate = 100;
+
+ static final int demoSize = 128;
+
+ static InsetsImmutable insets = null;
+ static int scrnHeight, scrnWidth;
+ static int num_x, num_y;
+
+ static int swapInterval = 0;
+
+ @BeforeClass
+ public static void initClass00() {
+ Window dummyWindow = NewtFactory.createWindow(new Capabilities());
+ dummyWindow.setSize(demoSize, demoSize);
+ dummyWindow.setVisible(true);
+ Assert.assertEquals(true, dummyWindow.isVisible());
+ Assert.assertEquals(true, dummyWindow.isNativeValid());
+ insets = dummyWindow.getInsets();
+ scrnHeight = dummyWindow.getScreen().getHeight();
+ scrnWidth = dummyWindow.getScreen().getWidth();
+ num_x = scrnWidth / ( demoSize + insets.getTotalWidth() ) - 2;
+ num_y = scrnHeight / ( demoSize + insets.getTotalHeight() ) - 2;
+ dummyWindow.destroy();
+ }
+
+ @AfterClass
+ public static void releaseClass00() {
+ }
+
+ protected abstract boolean isAWTTestCase();
+ protected abstract Thread getAWTRenderThread();
+ protected abstract AnimatorBase createAnimator();
+ protected abstract GLAutoDrawable createGLAutoDrawable(String title, int x, int y, int width, int height, GLCapabilitiesImmutable caps);
+ protected abstract void setGLAutoDrawableVisible(GLAutoDrawable[] glads);
+ protected abstract void destroyGLAutoDrawableVisible(GLAutoDrawable glad);
+
+ protected void runTestGL(GLCapabilitiesImmutable caps, int drawableCount, boolean exclusive, boolean preAdd, boolean shortenTest) throws InterruptedException {
+ final boolean useAWTRenderThread = isAWTTestCase();
+ if( useAWTRenderThread && exclusive ) {
+ if( testExclusiveWithAWT ) {
+ System.err.println("Warning: Testing AWT + Exclusive -> Not advised!");
+ } else {
+ System.err.println("Info: Skip test: AWT + Exclusive!");
+ return;
+ }
+ }
+ if( useAWTRenderThread && exclusive && !testExclusiveWithAWT) {
+ System.err.println("Skip test: AWT + Exclusive -> Not advised!");
+ return;
+ }
+ final Thread awtRenderThread = getAWTRenderThread();
+ final AnimatorBase animator = createAnimator();
+ if( !useAWTRenderThread ) {
+ animator.setModeBits(false, Animator.MODE_EXPECT_AWT_RENDERING_THREAD);
+ }
+ final GLAutoDrawable[] drawables = new GLAutoDrawable[drawableCount];
+ for(int i=0; i<drawableCount; i++) {
+ final int x = ( i % num_x ) * ( demoSize + insets.getTotalHeight() ) + insets.getLeftWidth();
+ final int y = ( (i / num_x) % num_y ) * ( demoSize + insets.getTotalHeight() ) + insets.getTopHeight();
+
+ drawables[i] = createGLAutoDrawable("Win #"+i, x, y, demoSize, demoSize, caps);
+ Assert.assertNotNull(drawables[i]);
+ final GearsES2 demo = new GearsES2(swapInterval);
+ demo.setVerbose(false);
+ drawables[i].addGLEventListener(demo);
+ }
+
+ if( preAdd ) {
+ for(int i=0; i<drawableCount; i++) {
+ animator.add(drawables[i]);
+ }
+ if( exclusive ) {
+ if( useAWTRenderThread ) {
+ Assert.assertEquals(null, animator.setExclusiveContext(awtRenderThread));
+ } else {
+ Assert.assertEquals(false, animator.setExclusiveContext(true));
+ }
+ }
+ }
+ Assert.assertFalse(animator.isAnimating());
+ Assert.assertFalse(animator.isStarted());
+
+ // Animator Start
+ Assert.assertTrue(animator.start());
+
+ Assert.assertTrue(animator.isStarted());
+ if( preAdd ) {
+ Assert.assertTrue(animator.isAnimating());
+ } else {
+ Assert.assertFalse(animator.isAnimating());
+ if( exclusive ) {
+ if( useAWTRenderThread ) {
+ Assert.assertEquals(null, animator.setExclusiveContext(awtRenderThread));
+ } else {
+ Assert.assertEquals(false, animator.setExclusiveContext(true));
+ }
+ }
+ for(int i=0; i<drawableCount; i++) {
+ animator.add(drawables[i]);
+ }
+ Assert.assertTrue(animator.isAnimating());
+ }
+ Assert.assertEquals(exclusive, animator.isExclusiveContextEnabled());
+
+ // After start, ExclusiveContextThread is set
+ {
+ final Thread ect = animator.getExclusiveContextThread();
+ if(exclusive) {
+ if( useAWTRenderThread ) {
+ Assert.assertEquals(awtRenderThread, ect);
+ } else {
+ Assert.assertEquals(animator.getThread(), ect);
+ }
+ } else {
+ Assert.assertEquals(null, ect);
+ }
+ for(int i=0; i<drawableCount; i++) {
+ Assert.assertEquals(ect, drawables[i].getExclusiveContextThread());
+ }
+ setGLAutoDrawableVisible(drawables);
+ }
+ animator.setUpdateFPSFrames(showFPSRate, showFPS ? System.err : null);
+
+ // Normal run ..
+ Thread.sleep(duration/durationParts); // 1
+
+ if( !shortenTest ) {
+ // Disable/Enable exclusive mode manually for all GLAutoDrawable
+ if(exclusive) {
+ final Thread ect = animator.getExclusiveContextThread();
+ if( useAWTRenderThread ) {
+ Assert.assertEquals(awtRenderThread, ect);
+ } else {
+ Assert.assertEquals(animator.getThread(), ect);
+ }
+ for(int i=0; i<drawableCount; i++) {
+ final Thread t = drawables[i].setExclusiveContextThread(null);
+ Assert.assertEquals(ect, t);
+ }
+
+ Thread.sleep(duration/durationParts); // 2
+
+ for(int i=0; i<drawableCount; i++) {
+ // poll until clearing drawable ECT is established
+ {
+ boolean ok = null == drawables[i].getExclusiveContextThread();
+ int c = 0;
+ while(!ok && c<5*50) { // 5*50*20 = 5s TO
+ Thread.sleep(20);
+ ok = null == drawables[i].getExclusiveContextThread();
+ c++;
+ }
+ if(c>0) {
+ System.err.println("Clearing drawable ECT was done 'later' @ "+(c*20)+"ms, ok "+ok);
+ }
+ Assert.assertEquals(true, ok);
+ }
+ final Thread t = drawables[i].setExclusiveContextThread(ect);
+ Assert.assertEquals(null, t);
+ }
+
+ Thread.sleep(duration/durationParts); // 3
+ }
+
+ // Disable/Enable exclusive mode via Animator for all GLAutoDrawable
+ if(exclusive) {
+ final Thread ect = animator.getExclusiveContextThread();
+ if( useAWTRenderThread ) {
+ Assert.assertEquals(awtRenderThread, ect);
+ } else {
+ Assert.assertEquals(animator.getThread(), ect);
+ }
+
+ Assert.assertEquals(true, animator.setExclusiveContext(false));
+ Assert.assertFalse(animator.isExclusiveContextEnabled());
+ for(int i=0; i<drawableCount; i++) {
+ Assert.assertEquals(null, drawables[i].getExclusiveContextThread());
+ }
+
+ Thread.sleep(duration/durationParts); // 4
+
+ Assert.assertEquals(null, animator.setExclusiveContext(ect));
+ Assert.assertTrue(animator.isExclusiveContextEnabled());
+ Assert.assertEquals(ect, animator.getExclusiveContextThread());
+ for(int i=0; i<drawableCount; i++) {
+ Assert.assertEquals(ect, drawables[i].getExclusiveContextThread());
+ }
+
+ Thread.sleep(duration/durationParts); // 5
+ }
+
+ Assert.assertEquals(exclusive, animator.isExclusiveContextEnabled());
+ Assert.assertTrue(animator.isStarted());
+ Assert.assertTrue(animator.isAnimating());
+ Assert.assertFalse(animator.isPaused());
+
+ // Animator Pause
+ Assert.assertTrue(animator.pause());
+ Assert.assertTrue(animator.isStarted());
+ Assert.assertFalse(animator.isAnimating());
+ Assert.assertTrue(animator.isPaused());
+ Assert.assertEquals(exclusive, animator.isExclusiveContextEnabled());
+ if(exclusive) {
+ final Thread ect = animator.getExclusiveContextThread();
+ if( useAWTRenderThread ) {
+ Assert.assertEquals(awtRenderThread, ect);
+ } else {
+ Assert.assertEquals(animator.getThread(), ect);
+ }
+ } else {
+ Assert.assertEquals(null, animator.getExclusiveContextThread());
+ }
+ for(int i=0; i<drawableCount; i++) {
+ Assert.assertEquals(null, drawables[i].getExclusiveContextThread());
+ }
+ Thread.sleep(duration/durationParts); // 6
+
+ // Animator Resume
+ Assert.assertTrue(animator.resume());
+ Assert.assertTrue(animator.isStarted());
+ Assert.assertTrue(animator.isAnimating());
+ Assert.assertFalse(animator.isPaused());
+ Assert.assertEquals(exclusive, animator.isExclusiveContextEnabled());
+ if(exclusive) {
+ final Thread ect = animator.getExclusiveContextThread();
+ if( useAWTRenderThread ) {
+ Assert.assertEquals(awtRenderThread, ect);
+ } else {
+ Assert.assertEquals(animator.getThread(), ect);
+ }
+ for(int i=0; i<drawableCount; i++) {
+ Assert.assertEquals(ect, drawables[i].getExclusiveContextThread());
+ }
+ } else {
+ Assert.assertEquals(null, animator.getExclusiveContextThread());
+ for(int i=0; i<drawableCount; i++) {
+ Assert.assertEquals(null, drawables[i].getExclusiveContextThread());
+ }
+ }
+ Thread.sleep(duration/durationParts); // 7
+
+ // Animator Stop #1
+ Assert.assertTrue(animator.stop());
+ Assert.assertFalse(animator.isAnimating());
+ Assert.assertFalse(animator.isStarted());
+ Assert.assertFalse(animator.isPaused());
+ Assert.assertEquals(exclusive, animator.isExclusiveContextEnabled());
+ Assert.assertEquals(null, animator.getExclusiveContextThread());
+ for(int i=0; i<drawableCount; i++) {
+ Assert.assertEquals(null, drawables[i].getExclusiveContextThread());
+ }
+ Thread.sleep(duration/durationParts); // 8
+
+ // Animator Re-Start
+ Assert.assertTrue(animator.start());
+ Assert.assertTrue(animator.isStarted());
+ Assert.assertTrue(animator.isAnimating());
+ Assert.assertEquals(exclusive, animator.isExclusiveContextEnabled());
+ // After start, ExclusiveContextThread is set
+ {
+ final Thread ect = animator.getExclusiveContextThread();
+ if(exclusive) {
+ if( useAWTRenderThread ) {
+ Assert.assertEquals(awtRenderThread, ect);
+ } else {
+ Assert.assertEquals(animator.getThread(), ect);
+ }
+ } else {
+ Assert.assertEquals(null, ect);
+ }
+ for(int i=0; i<drawableCount; i++) {
+ Assert.assertEquals(ect, drawables[i].getExclusiveContextThread());
+ }
+ }
+ Thread.sleep(duration/durationParts); // 9
+
+ // Remove all drawables .. while running!
+ for(int i=0; i<drawableCount; i++) {
+ final GLAutoDrawable drawable = drawables[i];
+ animator.remove(drawable);
+ Assert.assertEquals(null, drawable.getExclusiveContextThread());
+ }
+ Assert.assertTrue(animator.isStarted());
+ Assert.assertFalse(animator.isAnimating()); // no drawables in list!
+ } // !shortenTest
+
+ // Animator Stop #2
+ Assert.assertTrue(animator.stop());
+ Assert.assertFalse(animator.isAnimating());
+ Assert.assertFalse(animator.isStarted());
+ Assert.assertFalse(animator.isPaused());
+ Assert.assertEquals(exclusive, animator.isExclusiveContextEnabled());
+ Assert.assertEquals(null, animator.getExclusiveContextThread());
+
+ // Destroy GLWindows
+ for(int i=0; i<drawableCount; i++) {
+ destroyGLAutoDrawableVisible(drawables[i]);
+ Assert.assertEquals(true, AWTRobotUtil.waitForRealized(drawables[i], false));
+ }
+ }
+
+ @Test
+ public void test01NormalPre_1Win() throws InterruptedException {
+ final GLProfile glp = GLProfile.getGL2ES2();
+ final GLCapabilities caps = new GLCapabilities( glp );
+ runTestGL(caps, 1 /* numWin */, false /* exclusive */, true /* preAdd */, false /* short */);
+ }
+
+ @Test
+ public void test02NormalPost_1Win() throws InterruptedException {
+ final GLProfile glp = GLProfile.getGL2ES2();
+ final GLCapabilities caps = new GLCapabilities( glp );
+ runTestGL(caps, 1 /* numWin */, false /* exclusive */, false /* preAdd */, true /* short */);
+ }
+
+ @Test
+ public void test03ExclPre_1Win() throws InterruptedException {
+ final GLProfile glp = GLProfile.getGL2ES2();
+ final GLCapabilities caps = new GLCapabilities( glp );
+ runTestGL(caps, 1 /* numWin */, true /* exclusive */, true /* preAdd */, false /* short */);
+ }
+
+ @Test
+ public void test04ExclPost_1Win() throws InterruptedException {
+ final GLProfile glp = GLProfile.getGL2ES2();
+ final GLCapabilities caps = new GLCapabilities( glp );
+ runTestGL(caps, 1 /* numWin */, true /* exclusive */, false /* preAdd */, true /* short */);
+ }
+
+ @Test
+ public void test05NormalPre_4Win() throws InterruptedException {
+ final GLProfile glp = GLProfile.getGL2ES2();
+ final GLCapabilities caps = new GLCapabilities( glp );
+ runTestGL(caps, 4 /* numWin */, false /* exclusive */, true /* preAdd */, false /* short */);
+ }
+
+ @Test
+ public void test06NormalPost_4Win() throws InterruptedException {
+ final GLProfile glp = GLProfile.getGL2ES2();
+ final GLCapabilities caps = new GLCapabilities( glp );
+ runTestGL(caps, 4 /* numWin */, false /* exclusive */, false /* preAdd */, true /* short */);
+ }
+
+ @Test
+ public void test07ExclPre_4Win() throws InterruptedException {
+ final GLProfile glp = GLProfile.getGL2ES2();
+ final GLCapabilities caps = new GLCapabilities( glp );
+ runTestGL(caps, 4 /* numWin */, true /* exclusive */, true /* preAdd */, false /* short */);
+ }
+
+ @Test
+ public void test08ExclPost_4Win() throws InterruptedException {
+ final GLProfile glp = GLProfile.getGL2ES2();
+ final GLCapabilities caps = new GLCapabilities( glp );
+ runTestGL(caps, 4 /* numWin */, true /* exclusive */, false /* preAdd */, true /* short */);
+ }
+
+}
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/ExclusiveContextBase00AWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/ExclusiveContextBase00AWT.java
new file mode 100644
index 000000000..310f59f07
--- /dev/null
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/ExclusiveContextBase00AWT.java
@@ -0,0 +1,161 @@
+/**
+ * Copyright 2013 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+
+package com.jogamp.opengl.test.junit.jogl.acore;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.EventQueue;
+import java.awt.Frame;
+
+import javax.media.opengl.GLAutoDrawable;
+import javax.media.opengl.GLCapabilitiesImmutable;
+import javax.media.opengl.awt.GLCanvas;
+
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.AfterClass;
+
+import com.jogamp.common.os.Platform;
+import com.jogamp.common.util.VersionNumber;
+
+/**
+ * ExclusiveContextThread base implementation to test correctness of the ExclusiveContext feature _and_ AnimatorBase with AWT.
+ */
+public abstract class ExclusiveContextBase00AWT extends ExclusiveContextBase00 {
+
+ static Thread awtEDT;
+ static boolean osxCALayerAWTModBug;
+
+ @BeforeClass
+ public static void initClass00AWT() {
+
+ final VersionNumber version170 = new VersionNumber(1, 7, 0);
+ osxCALayerAWTModBug = Platform.OSType.MACOS == Platform.getOSType() &&
+ 0 > Platform.getJavaVersionNumber().compareTo(version170);
+ System.err.println("OSX CALayer AWT-Mod Bug "+osxCALayerAWTModBug);
+ System.err.println("OSType "+Platform.getOSType());
+ System.err.println("Java Version "+Platform.getJavaVersionNumber());
+
+ try {
+ EventQueue.invokeAndWait(new Runnable() {
+ public void run() {
+ awtEDT = Thread.currentThread();
+ } } );
+ } catch (Exception e) {
+ e.printStackTrace();
+ Assert.assertNull(e);
+ }
+
+ }
+
+ @AfterClass
+ public static void releaseClass00AWT() {
+ }
+
+ @Override
+ protected boolean isAWTTestCase() { return true; }
+
+ @Override
+ protected Thread getAWTRenderThread() {
+ return awtEDT;
+ }
+
+ @Override
+ protected GLAutoDrawable createGLAutoDrawable(final String title, final int x, final int y, final int width, final int height, GLCapabilitiesImmutable caps) {
+ final GLCanvas glCanvas = new GLCanvas();
+
+ // FIXME: Below AWT layouts freezes OSX/Java7 @ setVisible: Window.setVisible .. [email protected]
+ // final Dimension sz = new Dimension(width, height);
+ // glCanvas.setMinimumSize(sz);
+ // glCanvas.setPreferredSize(sz);
+ // glCanvas.setSize(sz);
+ try {
+ EventQueue.invokeAndWait(new Runnable() {
+ public void run() {
+ final Frame frame = new Frame();
+ frame.setLayout(new BorderLayout());
+ frame.setMinimumSize(new Dimension(width, height));
+ frame.setBounds(x, y, width, height);
+ frame.add(glCanvas, BorderLayout.CENTER);
+ // frame.pack();
+ frame.validate();
+ if( !osxCALayerAWTModBug ) {
+ frame.setTitle(title);
+ }
+ } });
+ } catch (Exception e) {
+ e.printStackTrace();
+ Assert.assertNull(e);
+ }
+
+ return glCanvas;
+ }
+
+ protected Frame getFrame(GLAutoDrawable glad) {
+ Container p = ((Component)glad).getParent();
+ while( null != p && !( p instanceof Frame ) ) {
+ p = p.getParent();
+ }
+ return (Frame)p;
+ }
+
+ @Override
+ protected void setGLAutoDrawableVisible(final GLAutoDrawable[] glads) {
+ try {
+ EventQueue.invokeAndWait(new Runnable() {
+ public void run() {
+ final int count = glads.length;
+ for(int i=0; i<count; i++) {
+ final GLAutoDrawable glad = glads[i];
+ final Frame frame = getFrame(glad);
+ frame.setVisible(true);
+ }
+ } } );
+ } catch (Exception e) {
+ e.printStackTrace();
+ Assert.assertNull(e);
+ }
+ }
+
+ @Override
+ protected void destroyGLAutoDrawableVisible(GLAutoDrawable glad) {
+ final Frame frame = getFrame(glad);
+ try {
+ EventQueue.invokeAndWait(new Runnable() {
+ public void run() {
+ frame.dispose();
+ } } );
+ } catch (Exception e) {
+ e.printStackTrace();
+ Assert.assertNull(e);
+ }
+ }
+}
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/ExclusiveContextBase00NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/ExclusiveContextBase00NEWT.java
new file mode 100644
index 000000000..40a79f484
--- /dev/null
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/ExclusiveContextBase00NEWT.java
@@ -0,0 +1,94 @@
+/**
+ * Copyright 2013 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+
+package com.jogamp.opengl.test.junit.jogl.acore;
+
+import com.jogamp.newt.Display;
+import com.jogamp.newt.NewtFactory;
+import com.jogamp.newt.Screen;
+import com.jogamp.newt.opengl.GLWindow;
+
+import javax.media.opengl.GLAutoDrawable;
+import javax.media.opengl.GLCapabilitiesImmutable;
+
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.AfterClass;
+
+/**
+ * ExclusiveContextThread base implementation to test correctness of the ExclusiveContext feature _and_ AnimatorBase with NEWT.
+ */
+public abstract class ExclusiveContextBase00NEWT extends ExclusiveContextBase00 {
+
+ static Display dpy;
+ static Screen screen;
+
+ @BeforeClass
+ public static void initClass00NEWT() {
+ dpy = NewtFactory.createDisplay(null);
+ screen = NewtFactory.createScreen(dpy, 0);
+ }
+
+ @AfterClass
+ public static void releaseClass00NEWT() {
+ screen = null;
+ dpy = null;
+ }
+
+ @Override
+ protected boolean isAWTTestCase() { return false; }
+
+ @Override
+ protected Thread getAWTRenderThread() {
+ return null;
+ }
+
+ @Override
+ protected GLAutoDrawable createGLAutoDrawable(String title, int x, int y, int width, int height, GLCapabilitiesImmutable caps) {
+ GLWindow glWindow = GLWindow.create(screen, caps);
+ Assert.assertNotNull(glWindow);
+ glWindow.setTitle(title);
+ glWindow.setSize(width, height);
+ glWindow.setPosition(x, y);
+ return glWindow;
+ }
+
+ @Override
+ protected void setGLAutoDrawableVisible(GLAutoDrawable[] glads) {
+ final int count = glads.length;
+ for(int i=0; i<count; i++) {
+ final GLAutoDrawable glad = glads[i];
+ ((GLWindow)glad).setVisible(true);
+ }
+ }
+
+ @Override
+ protected void destroyGLAutoDrawableVisible(GLAutoDrawable glad) {
+ ((GLWindow)glad).destroy();
+ }
+}
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/ExclusiveContextBase10.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/ExclusiveContextBase10.java
new file mode 100644
index 000000000..4bad6c84f
--- /dev/null
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/ExclusiveContextBase10.java
@@ -0,0 +1,213 @@
+/**
+ * Copyright 2013 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+
+package com.jogamp.opengl.test.junit.jogl.acore;
+
+import com.jogamp.newt.NewtFactory;
+import com.jogamp.newt.Window;
+import com.jogamp.opengl.test.junit.util.AWTRobotUtil;
+import com.jogamp.opengl.test.junit.util.UITestCase;
+
+import com.jogamp.opengl.util.Animator;
+import com.jogamp.opengl.util.AnimatorBase;
+
+import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2;
+
+import javax.media.nativewindow.Capabilities;
+import javax.media.nativewindow.util.InsetsImmutable;
+
+import javax.media.opengl.GLAutoDrawable;
+import javax.media.opengl.GLCapabilities;
+import javax.media.opengl.GLCapabilitiesImmutable;
+import javax.media.opengl.GLProfile;
+
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.AfterClass;
+import org.junit.Test;
+
+/**
+ * ExclusiveContextThread base implementation to test performance impact of the ExclusiveContext feature with AnimatorBase.
+ */
+public abstract class ExclusiveContextBase10 extends UITestCase {
+ static boolean testExclusiveWithAWT = false;
+ static long duration = 1400;
+
+ static boolean showFPS = true;
+ static int showFPSRate = 60;
+
+ static final int demoSize = 128;
+
+ static InsetsImmutable insets = null;
+ static int scrnHeight, scrnWidth;
+ static int num_x, num_y;
+
+ static int swapInterval = 0;
+
+ @BeforeClass
+ public static void initClass00() {
+ Window dummyWindow = NewtFactory.createWindow(new Capabilities());
+ dummyWindow.setSize(demoSize, demoSize);
+ dummyWindow.setVisible(true);
+ Assert.assertEquals(true, dummyWindow.isVisible());
+ Assert.assertEquals(true, dummyWindow.isNativeValid());
+ insets = dummyWindow.getInsets();
+ scrnHeight = dummyWindow.getScreen().getHeight();
+ scrnWidth = dummyWindow.getScreen().getWidth();
+ num_x = scrnWidth / ( demoSize + insets.getTotalWidth() ) - 2;
+ num_y = scrnHeight / ( demoSize + insets.getTotalHeight() ) - 2;
+ dummyWindow.destroy();
+ }
+
+ @AfterClass
+ public static void releaseClass00() {
+ }
+
+ protected abstract boolean isAWTTestCase();
+ protected abstract Thread getAWTRenderThread();
+ protected abstract AnimatorBase createAnimator();
+ protected abstract GLAutoDrawable createGLAutoDrawable(String title, int x, int y, int width, int height, GLCapabilitiesImmutable caps);
+ protected abstract void setGLAutoDrawableVisible(GLAutoDrawable[] glads);
+ protected abstract void destroyGLAutoDrawableVisible(GLAutoDrawable glad);
+
+ protected void runTestGL(GLCapabilitiesImmutable caps, int drawableCount, boolean exclusive) throws InterruptedException {
+ final boolean useAWTRenderThread = isAWTTestCase();
+ if( useAWTRenderThread && exclusive ) {
+ if( testExclusiveWithAWT ) {
+ System.err.println("Warning: Testing AWT + Exclusive -> Not advised!");
+ } else {
+ System.err.println("Info: Skip test: AWT + Exclusive!");
+ return;
+ }
+ }
+ if( useAWTRenderThread && exclusive && !testExclusiveWithAWT) {
+ System.err.println("Skip test: AWT + Exclusive -> Not advised!");
+ return;
+ }
+ final Thread awtRenderThread = getAWTRenderThread();
+ final AnimatorBase animator = createAnimator();
+ if( !useAWTRenderThread ) {
+ animator.setModeBits(false, Animator.MODE_EXPECT_AWT_RENDERING_THREAD);
+ }
+ final GLAutoDrawable[] drawables = new GLAutoDrawable[drawableCount];
+ for(int i=0; i<drawableCount; i++) {
+ final int x = ( i % num_x ) * ( demoSize + insets.getTotalHeight() ) + insets.getLeftWidth();
+ final int y = ( (i / num_x) % num_y ) * ( demoSize + insets.getTotalHeight() ) + insets.getTopHeight();
+
+ drawables[i] = createGLAutoDrawable("Win #"+i, x, y, demoSize, demoSize, caps);
+ Assert.assertNotNull(drawables[i]);
+ final GearsES2 demo = new GearsES2(swapInterval);
+ demo.setVerbose(false);
+ drawables[i].addGLEventListener(demo);
+ }
+
+ for(int i=0; i<drawableCount; i++) {
+ animator.add(drawables[i]);
+ }
+ if( exclusive ) {
+ if( useAWTRenderThread ) {
+ Assert.assertEquals(null, animator.setExclusiveContext(awtRenderThread));
+ } else {
+ Assert.assertEquals(false, animator.setExclusiveContext(true));
+ }
+ }
+ Assert.assertFalse(animator.isAnimating());
+ Assert.assertFalse(animator.isStarted());
+
+ // Animator Start
+ Assert.assertTrue(animator.start());
+
+ Assert.assertTrue(animator.isStarted());
+ Assert.assertTrue(animator.isAnimating());
+ Assert.assertEquals(exclusive, animator.isExclusiveContextEnabled());
+
+ // After start, ExclusiveContextThread is set
+ {
+ final Thread ect = animator.getExclusiveContextThread();
+ if(exclusive) {
+ if( useAWTRenderThread ) {
+ Assert.assertEquals(awtRenderThread, ect);
+ } else {
+ Assert.assertEquals(animator.getThread(), ect);
+ }
+ } else {
+ Assert.assertEquals(null, ect);
+ }
+ for(int i=0; i<drawableCount; i++) {
+ Assert.assertEquals(ect, drawables[i].getExclusiveContextThread());
+ }
+ setGLAutoDrawableVisible(drawables);
+ }
+ animator.setUpdateFPSFrames(showFPSRate, showFPS ? System.err : null);
+
+ // Normal run ..
+ Thread.sleep(duration);
+
+ // Animator Stop #2
+ Assert.assertTrue(animator.stop());
+ Assert.assertFalse(animator.isAnimating());
+ Assert.assertFalse(animator.isStarted());
+ Assert.assertFalse(animator.isPaused());
+ Assert.assertEquals(exclusive, animator.isExclusiveContextEnabled());
+ Assert.assertEquals(null, animator.getExclusiveContextThread());
+
+ // Destroy GLWindows
+ for(int i=0; i<drawableCount; i++) {
+ destroyGLAutoDrawableVisible(drawables[i]);
+ Assert.assertEquals(true, AWTRobotUtil.waitForRealized(drawables[i], false));
+ }
+ }
+
+ @Test
+ public void test01Normal_1Win() throws InterruptedException {
+ final GLProfile glp = GLProfile.getGL2ES2();
+ final GLCapabilities caps = new GLCapabilities( glp );
+ runTestGL(caps, 1 /* numWin */, false /* exclusive */);
+ }
+
+ @Test
+ public void test03Excl_1Win() throws InterruptedException {
+ final GLProfile glp = GLProfile.getGL2ES2();
+ final GLCapabilities caps = new GLCapabilities( glp );
+ runTestGL(caps, 1 /* numWin */, true /* exclusive */);
+ }
+
+ @Test
+ public void test05Normal_4Win() throws InterruptedException {
+ final GLProfile glp = GLProfile.getGL2ES2();
+ final GLCapabilities caps = new GLCapabilities( glp );
+ runTestGL(caps, 4 /* numWin */, false /* exclusive */);
+ }
+
+ @Test
+ public void test07Excl_4Win() throws InterruptedException {
+ final GLProfile glp = GLProfile.getGL2ES2();
+ final GLCapabilities caps = new GLCapabilities( glp );
+ runTestGL(caps, 4 /* numWin */, true /* exclusive */);
+ }
+}
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/ExclusiveContextBase10AWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/ExclusiveContextBase10AWT.java
new file mode 100644
index 000000000..5c4e9fd32
--- /dev/null
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/ExclusiveContextBase10AWT.java
@@ -0,0 +1,161 @@
+/**
+ * Copyright 2013 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+
+package com.jogamp.opengl.test.junit.jogl.acore;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.EventQueue;
+import java.awt.Frame;
+
+import javax.media.opengl.GLAutoDrawable;
+import javax.media.opengl.GLCapabilitiesImmutable;
+import javax.media.opengl.awt.GLCanvas;
+
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.AfterClass;
+
+import com.jogamp.common.os.Platform;
+import com.jogamp.common.util.VersionNumber;
+
+/**
+ * ExclusiveContextThread base implementation to test performance impact of the ExclusiveContext feature with AnimatorBase and AWT.
+ */
+public abstract class ExclusiveContextBase10AWT extends ExclusiveContextBase10 {
+
+ static Thread awtEDT;
+ static boolean osxCALayerAWTModBug;
+
+ @BeforeClass
+ public static void initClass00AWT() {
+
+ final VersionNumber version170 = new VersionNumber(1, 7, 0);
+ osxCALayerAWTModBug = Platform.OSType.MACOS == Platform.getOSType() &&
+ 0 > Platform.getJavaVersionNumber().compareTo(version170);
+ System.err.println("OSX CALayer AWT-Mod Bug "+osxCALayerAWTModBug);
+ System.err.println("OSType "+Platform.getOSType());
+ System.err.println("Java Version "+Platform.getJavaVersionNumber());
+
+ try {
+ EventQueue.invokeAndWait(new Runnable() {
+ public void run() {
+ awtEDT = Thread.currentThread();
+ } } );
+ } catch (Exception e) {
+ e.printStackTrace();
+ Assert.assertNull(e);
+ }
+
+ }
+
+ @AfterClass
+ public static void releaseClass00AWT() {
+ }
+
+ @Override
+ protected boolean isAWTTestCase() { return true; }
+
+ @Override
+ protected Thread getAWTRenderThread() {
+ return awtEDT;
+ }
+
+ @Override
+ protected GLAutoDrawable createGLAutoDrawable(final String title, final int x, final int y, final int width, final int height, GLCapabilitiesImmutable caps) {
+ final GLCanvas glCanvas = new GLCanvas();
+
+ // FIXME: Below AWT layouts freezes OSX/Java7 @ setVisible: Window.setVisible .. [email protected]
+ // final Dimension sz = new Dimension(width, height);
+ // glCanvas.setMinimumSize(sz);
+ // glCanvas.setPreferredSize(sz);
+ // glCanvas.setSize(sz);
+ try {
+ EventQueue.invokeAndWait(new Runnable() {
+ public void run() {
+ final Frame frame = new Frame();
+ frame.setLayout(new BorderLayout());
+ frame.setMinimumSize(new Dimension(width, height));
+ frame.setBounds(x, y, width, height);
+ frame.add(glCanvas, BorderLayout.CENTER);
+ // frame.pack();
+ frame.validate();
+ if( !osxCALayerAWTModBug ) {
+ frame.setTitle(title);
+ }
+ } });
+ } catch (Exception e) {
+ e.printStackTrace();
+ Assert.assertNull(e);
+ }
+
+ return glCanvas;
+ }
+
+ protected Frame getFrame(GLAutoDrawable glad) {
+ Container p = ((Component)glad).getParent();
+ while( null != p && !( p instanceof Frame ) ) {
+ p = p.getParent();
+ }
+ return (Frame)p;
+ }
+
+ @Override
+ protected void setGLAutoDrawableVisible(final GLAutoDrawable[] glads) {
+ try {
+ EventQueue.invokeAndWait(new Runnable() {
+ public void run() {
+ final int count = glads.length;
+ for(int i=0; i<count; i++) {
+ final GLAutoDrawable glad = glads[i];
+ final Frame frame = getFrame(glad);
+ frame.setVisible(true);
+ }
+ } } );
+ } catch (Exception e) {
+ e.printStackTrace();
+ Assert.assertNull(e);
+ }
+ }
+
+ @Override
+ protected void destroyGLAutoDrawableVisible(GLAutoDrawable glad) {
+ final Frame frame = getFrame(glad);
+ try {
+ EventQueue.invokeAndWait(new Runnable() {
+ public void run() {
+ frame.dispose();
+ } } );
+ } catch (Exception e) {
+ e.printStackTrace();
+ Assert.assertNull(e);
+ }
+ }
+}
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/ExclusiveContextBase10NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/ExclusiveContextBase10NEWT.java
new file mode 100644
index 000000000..ba95b597d
--- /dev/null
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/ExclusiveContextBase10NEWT.java
@@ -0,0 +1,94 @@
+/**
+ * Copyright 2013 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+
+package com.jogamp.opengl.test.junit.jogl.acore;
+
+import com.jogamp.newt.Display;
+import com.jogamp.newt.NewtFactory;
+import com.jogamp.newt.Screen;
+import com.jogamp.newt.opengl.GLWindow;
+
+import javax.media.opengl.GLAutoDrawable;
+import javax.media.opengl.GLCapabilitiesImmutable;
+
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.AfterClass;
+
+/**
+ * ExclusiveContextThread base implementation to test performance impact of the ExclusiveContext feature with AnimatorBase and NEWT.
+ */
+public abstract class ExclusiveContextBase10NEWT extends ExclusiveContextBase10 {
+
+ static Display dpy;
+ static Screen screen;
+
+ @BeforeClass
+ public static void initClass00NEWT() {
+ dpy = NewtFactory.createDisplay(null);
+ screen = NewtFactory.createScreen(dpy, 0);
+ }
+
+ @AfterClass
+ public static void releaseClass00NEWT() {
+ screen = null;
+ dpy = null;
+ }
+
+ @Override
+ protected boolean isAWTTestCase() { return false; }
+
+ @Override
+ protected Thread getAWTRenderThread() {
+ return null;
+ }
+
+ @Override
+ protected GLAutoDrawable createGLAutoDrawable(String title, int x, int y, int width, int height, GLCapabilitiesImmutable caps) {
+ GLWindow glWindow = GLWindow.create(screen, caps);
+ Assert.assertNotNull(glWindow);
+ glWindow.setTitle(title);
+ glWindow.setSize(width, height);
+ glWindow.setPosition(x, y);
+ return glWindow;
+ }
+
+ @Override
+ protected void setGLAutoDrawableVisible(GLAutoDrawable[] glads) {
+ final int count = glads.length;
+ for(int i=0; i<count; i++) {
+ final GLAutoDrawable glad = glads[i];
+ ((GLWindow)glad).setVisible(true);
+ }
+ }
+
+ @Override
+ protected void destroyGLAutoDrawableVisible(GLAutoDrawable glad) {
+ ((GLWindow)glad).destroy();
+ }
+}
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/InitConcurrentBaseNEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/InitConcurrentBaseNEWT.java
index 6b2de8366..f8e6ee5d3 100644
--- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/InitConcurrentBaseNEWT.java
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/InitConcurrentBaseNEWT.java
@@ -53,7 +53,7 @@ import com.jogamp.opengl.util.Animator;
* Rendering is always lock-free and independent of the EDT.
* </p>
*/
-public class InitConcurrentBaseNEWT extends UITestCase {
+public abstract class InitConcurrentBaseNEWT extends UITestCase {
static final int demoSize = 128;
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestExclusiveContext01VSyncAnimAWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestExclusiveContext01VSyncAnimAWT.java
new file mode 100644
index 000000000..245d42031
--- /dev/null
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestExclusiveContext01VSyncAnimAWT.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright 2013 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+
+package com.jogamp.opengl.test.junit.jogl.acore;
+
+import java.io.IOException;
+
+import com.jogamp.opengl.test.junit.util.MiscUtils;
+
+import com.jogamp.opengl.util.Animator;
+import com.jogamp.opengl.util.AnimatorBase;
+
+/**
+ * ExclusiveContextThread VSync Animator to test correctness of the ExclusiveContext feature _and_ Animator with AWT.
+ */
+public class TestExclusiveContext01VSyncAnimAWT extends ExclusiveContextBase00AWT {
+
+ @Override
+ protected AnimatorBase createAnimator() {
+ return new Animator();
+ }
+
+ public static void main(String args[]) throws IOException {
+ for(int i=0; i<args.length; i++) {
+ if(args[i].equals("-time")) {
+ i++;
+ duration = MiscUtils.atol(args[i], duration);
+ } else if(args[i].equals("-vsync")) {
+ i++;
+ swapInterval = MiscUtils.atoi(args[i], swapInterval);
+ } else if(args[i].equals("-showFPS")) {
+ i++;
+ showFPS = MiscUtils.atoi(args[i], showFPS ? 1 : 0) == 0 ? false : true ;
+ } else if(args[i].equals("-forceExclusiveTest")) {
+ testExclusiveWithAWT = true;
+ }
+ }
+ System.err.println("duration "+duration);
+ System.err.println("showFPS "+showFPS);
+ System.err.println("swapInterval "+swapInterval);
+ System.err.println("testExclusiveWithAWT "+testExclusiveWithAWT);
+
+ org.junit.runner.JUnitCore.main(TestExclusiveContext01VSyncAnimAWT.class.getName());
+ }
+}
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestExclusiveContext01VSyncAnimNEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestExclusiveContext01VSyncAnimNEWT.java
new file mode 100644
index 000000000..2cc423332
--- /dev/null
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestExclusiveContext01VSyncAnimNEWT.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright 2013 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+
+package com.jogamp.opengl.test.junit.jogl.acore;
+
+import java.io.IOException;
+
+import com.jogamp.opengl.test.junit.util.MiscUtils;
+import com.jogamp.opengl.util.Animator;
+import com.jogamp.opengl.util.AnimatorBase;
+
+
+/**
+ * ExclusiveContextThread VSync Animator to test correctness of the ExclusiveContext feature _and_ Animator with NEWT.
+ */
+public class TestExclusiveContext01VSyncAnimNEWT extends ExclusiveContextBase00NEWT {
+
+ @Override
+ protected AnimatorBase createAnimator() {
+ return new Animator();
+ }
+
+ public static void main(String args[]) throws IOException {
+ for(int i=0; i<args.length; i++) {
+ if(args[i].equals("-time")) {
+ i++;
+ duration = MiscUtils.atol(args[i], duration);
+ } else if(args[i].equals("-vsync")) {
+ i++;
+ swapInterval = MiscUtils.atoi(args[i], swapInterval);
+ } else if(args[i].equals("-showFPS")) {
+ i++;
+ showFPS = MiscUtils.atoi(args[i], showFPS ? 1 : 0) == 0 ? false : true ;
+ }
+ }
+ System.err.println("duration "+duration);
+ System.err.println("showFPS "+showFPS);
+ System.err.println("swapInterval "+swapInterval);
+
+ org.junit.runner.JUnitCore.main(TestExclusiveContext01VSyncAnimNEWT.class.getName());
+ }
+}
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestExclusiveContext02FPSAnimAWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestExclusiveContext02FPSAnimAWT.java
new file mode 100644
index 000000000..5e8e1ba07
--- /dev/null
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestExclusiveContext02FPSAnimAWT.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright 2013 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+
+package com.jogamp.opengl.test.junit.jogl.acore;
+
+import java.io.IOException;
+
+import com.jogamp.opengl.test.junit.util.MiscUtils;
+import com.jogamp.opengl.util.AnimatorBase;
+import com.jogamp.opengl.util.FPSAnimator;
+
+
+/**
+ * ExclusiveContextThread FPS Animator to test correctness of the ExclusiveContext feature _and_ FPSAnimator with AWT.
+ */
+public class TestExclusiveContext02FPSAnimAWT extends ExclusiveContextBase00AWT {
+
+ @Override
+ protected AnimatorBase createAnimator() {
+ return new FPSAnimator(0);
+ }
+
+ public static void main(String args[]) throws IOException {
+ for(int i=0; i<args.length; i++) {
+ if(args[i].equals("-time")) {
+ i++;
+ duration = MiscUtils.atol(args[i], duration);
+ } else if(args[i].equals("-vsync")) {
+ i++;
+ swapInterval = MiscUtils.atoi(args[i], swapInterval);
+ } else if(args[i].equals("-showFPS")) {
+ i++;
+ showFPS = MiscUtils.atoi(args[i], showFPS ? 1 : 0) == 0 ? false : true ;
+ } else if(args[i].equals("-forceExclusiveTest")) {
+ testExclusiveWithAWT = true;
+ }
+ }
+ System.err.println("duration "+duration);
+ System.err.println("showFPS "+showFPS);
+ System.err.println("swapInterval "+swapInterval);
+ System.err.println("testExclusiveWithAWT "+testExclusiveWithAWT);
+
+ org.junit.runner.JUnitCore.main(TestExclusiveContext02FPSAnimAWT.class.getName());
+ }
+}
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestExclusiveContext02FPSAnimNEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestExclusiveContext02FPSAnimNEWT.java
new file mode 100644
index 000000000..26fb7814e
--- /dev/null
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestExclusiveContext02FPSAnimNEWT.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright 2013 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+
+package com.jogamp.opengl.test.junit.jogl.acore;
+
+import java.io.IOException;
+
+import com.jogamp.opengl.test.junit.util.MiscUtils;
+import com.jogamp.opengl.util.AnimatorBase;
+import com.jogamp.opengl.util.FPSAnimator;
+
+
+/**
+ * ExclusiveContextThread FPS Animator to test correctness of the ExclusiveContext feature _and_ FPSAnimator with NEWT.
+ */
+public class TestExclusiveContext02FPSAnimNEWT extends ExclusiveContextBase00NEWT {
+
+ @Override
+ protected AnimatorBase createAnimator() {
+ return new FPSAnimator(0);
+ }
+
+ public static void main(String args[]) throws IOException {
+ for(int i=0; i<args.length; i++) {
+ if(args[i].equals("-time")) {
+ i++;
+ duration = MiscUtils.atol(args[i], duration);
+ } else if(args[i].equals("-vsync")) {
+ i++;
+ swapInterval = MiscUtils.atoi(args[i], swapInterval);
+ } else if(args[i].equals("-showFPS")) {
+ i++;
+ showFPS = MiscUtils.atoi(args[i], showFPS ? 1 : 0) == 0 ? false : true ;
+ }
+ }
+ System.err.println("duration "+duration);
+ System.err.println("showFPS "+showFPS);
+ System.err.println("swapInterval "+swapInterval);
+
+ org.junit.runner.JUnitCore.main(TestExclusiveContext02FPSAnimNEWT.class.getName());
+ }
+}
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestExclusiveContext11VSyncAnimNEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestExclusiveContext11VSyncAnimNEWT.java
new file mode 100644
index 000000000..3a7a130ea
--- /dev/null
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestExclusiveContext11VSyncAnimNEWT.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright 2013 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+
+package com.jogamp.opengl.test.junit.jogl.acore;
+
+import java.io.IOException;
+
+import com.jogamp.opengl.test.junit.util.MiscUtils;
+import com.jogamp.opengl.util.Animator;
+import com.jogamp.opengl.util.AnimatorBase;
+
+/**
+ * ExclusiveContextThread base implementation to test performance impact of the ExclusiveContext feature with Animator and NEWT.
+ */
+public class TestExclusiveContext11VSyncAnimNEWT extends ExclusiveContextBase10NEWT {
+
+ @Override
+ protected AnimatorBase createAnimator() {
+ return new Animator();
+ }
+
+ public static void main(String args[]) throws IOException {
+ for(int i=0; i<args.length; i++) {
+ if(args[i].equals("-time")) {
+ i++;
+ duration = MiscUtils.atol(args[i], duration);
+ } else if(args[i].equals("-vsync")) {
+ i++;
+ swapInterval = MiscUtils.atoi(args[i], swapInterval);
+ } else if(args[i].equals("-showFPS")) {
+ i++;
+ showFPS = MiscUtils.atoi(args[i], showFPS ? 1 : 0) == 0 ? false : true ;
+ }
+ }
+ System.err.println("duration "+duration);
+ System.err.println("showFPS "+showFPS);
+ System.err.println("swapInterval "+swapInterval);
+
+ org.junit.runner.JUnitCore.main(TestExclusiveContext11VSyncAnimNEWT.class.getName());
+ }
+}
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestExclusiveContext12FPSAnimNEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestExclusiveContext12FPSAnimNEWT.java
new file mode 100644
index 000000000..3f99e3fb7
--- /dev/null
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestExclusiveContext12FPSAnimNEWT.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright 2013 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+
+package com.jogamp.opengl.test.junit.jogl.acore;
+
+import java.io.IOException;
+
+import com.jogamp.opengl.test.junit.util.MiscUtils;
+import com.jogamp.opengl.util.AnimatorBase;
+import com.jogamp.opengl.util.FPSAnimator;
+
+
+/**
+ * ExclusiveContextThread base implementation to test performance impact of the ExclusiveContext feature with FPSAnimator and NEWT.
+ */
+public class TestExclusiveContext12FPSAnimNEWT extends ExclusiveContextBase10NEWT {
+
+ @Override
+ protected AnimatorBase createAnimator() {
+ return new FPSAnimator(0);
+ }
+
+ public static void main(String args[]) throws IOException {
+ for(int i=0; i<args.length; i++) {
+ if(args[i].equals("-time")) {
+ i++;
+ duration = MiscUtils.atol(args[i], duration);
+ } else if(args[i].equals("-vsync")) {
+ i++;
+ swapInterval = MiscUtils.atoi(args[i], swapInterval);
+ } else if(args[i].equals("-showFPS")) {
+ i++;
+ showFPS = MiscUtils.atoi(args[i], showFPS ? 1 : 0) == 0 ? false : true ;
+ }
+ }
+ System.err.println("duration "+duration);
+ System.err.println("showFPS "+showFPS);
+ System.err.println("swapInterval "+swapInterval);
+
+ org.junit.runner.JUnitCore.main(TestExclusiveContext12FPSAnimNEWT.class.getName());
+ }
+}
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es1/newt/TestGearsES1NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es1/newt/TestGearsES1NEWT.java
index 9e0bbae71..05cc2aeb0 100644
--- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es1/newt/TestGearsES1NEWT.java
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es1/newt/TestGearsES1NEWT.java
@@ -31,6 +31,7 @@ package com.jogamp.opengl.test.junit.jogl.demos.es1.newt;
import com.jogamp.newt.event.KeyAdapter;
import com.jogamp.newt.event.KeyEvent;
import com.jogamp.newt.opengl.GLWindow;
+import com.jogamp.opengl.test.junit.util.MiscUtils;
import com.jogamp.opengl.test.junit.util.UITestCase;
import com.jogamp.opengl.test.junit.util.QuitAdapter;
@@ -50,6 +51,7 @@ public class TestGearsES1NEWT extends UITestCase {
static int width, height;
static boolean forceES2 = false;
static boolean forceFFPEmu = false;
+ static int swapInterval = 1;
@BeforeClass
public static void initClass() {
@@ -66,7 +68,7 @@ public class TestGearsES1NEWT extends UITestCase {
Assert.assertNotNull(glWindow);
glWindow.setTitle("Gears NEWT Test");
- final GearsES1 demo = new GearsES1();
+ final GearsES1 demo = new GearsES1(swapInterval);
demo.setForceFFPEmu(forceFFPEmu, forceFFPEmu, false, false);
glWindow.addGLEventListener(demo);
final SnapshotGLEventListener snap = new SnapshotGLEventListener();
@@ -117,7 +119,7 @@ public class TestGearsES1NEWT extends UITestCase {
runTestGL(caps, forceFFPEmu);
}
- static long duration = 1000; // ms
+ static long duration = 500; // ms
public static void main(String args[]) {
for(int i=0; i<args.length; i++) {
@@ -126,6 +128,9 @@ public class TestGearsES1NEWT extends UITestCase {
try {
duration = Integer.parseInt(args[i]);
} catch (Exception ex) { ex.printStackTrace(); }
+ } else if(args[i].equals("-vsync")) {
+ i++;
+ swapInterval = MiscUtils.atoi(args[i], swapInterval);
} else if(args[i].equals("-es2")) {
forceES2 = true;
} else if(args[i].equals("-ffpemu")) {
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java
index 21c9f3535..cc5aae99e 100644
--- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java
@@ -65,7 +65,9 @@ public class GearsES2 implements GLEventListener {
private int prevMouseX, prevMouseY;
private boolean doRotate = true;
- boolean ignoreFocus = false;
+ private boolean ignoreFocus = false;
+ private boolean clearBuffers = true;
+ private boolean verbose = true;
public GearsES2(int swapInterval) {
this.swapInterval = swapInterval;
@@ -77,6 +79,8 @@ public class GearsES2 implements GLEventListener {
public void setIgnoreFocus(boolean v) { ignoreFocus = v; }
public void setDoRotation(boolean rotate) { this.doRotate = rotate; }
+ public void setClearBuffers(boolean v) { clearBuffers = v; }
+ public void setVerbose(boolean v) { verbose = v; }
public void setPMVUseBackingArray(boolean pmvUseBackingArray) {
this.pmvUseBackingArray = pmvUseBackingArray;
@@ -108,17 +112,19 @@ public class GearsES2 implements GLEventListener {
System.err.println(Thread.currentThread()+" GearsES2.init ...");
GL2ES2 gl = drawable.getGL().getGL2ES2();
- System.err.println("GearsES2 init on "+Thread.currentThread());
- System.err.println("Chosen GLCapabilities: " + drawable.getChosenGLCapabilities());
- System.err.println("INIT GL IS: " + gl.getClass().getName());
- System.err.println("GL_VENDOR: " + gl.glGetString(GL.GL_VENDOR));
- System.err.println("GL_RENDERER: " + gl.glGetString(GL.GL_RENDERER));
- System.err.println("GL_VERSION: " + gl.glGetString(GL.GL_VERSION));
- System.err.println("GL GLSL: "+gl.hasGLSL()+", has-compiler: "+gl.isFunctionAvailable("glCompileShader")+", version "+(gl.hasGLSL() ? gl.glGetString(GL2ES2.GL_SHADING_LANGUAGE_VERSION) : "none")+", "+gl.getContext().getGLSLVersionNumber());
- System.err.println("GL FBO: basic "+ gl.hasBasicFBOSupport()+", full "+gl.hasFullFBOSupport());
- System.err.println("GL Profile: "+gl.getGLProfile());
- System.err.println("GL Renderer Quirks:" + gl.getContext().getRendererQuirks().toString());
- System.err.println("GL:" + gl + ", " + gl.getContext().getGLVersion());
+ if(verbose) {
+ System.err.println("GearsES2 init on "+Thread.currentThread());
+ System.err.println("Chosen GLCapabilities: " + drawable.getChosenGLCapabilities());
+ System.err.println("INIT GL IS: " + gl.getClass().getName());
+ System.err.println("GL_VENDOR: " + gl.glGetString(GL.GL_VENDOR));
+ System.err.println("GL_RENDERER: " + gl.glGetString(GL.GL_RENDERER));
+ System.err.println("GL_VERSION: " + gl.glGetString(GL.GL_VERSION));
+ System.err.println("GL GLSL: "+gl.hasGLSL()+", has-compiler: "+gl.isFunctionAvailable("glCompileShader")+", version "+(gl.hasGLSL() ? gl.glGetString(GL2ES2.GL_SHADING_LANGUAGE_VERSION) : "none")+", "+gl.getContext().getGLSLVersionNumber());
+ System.err.println("GL FBO: basic "+ gl.hasBasicFBOSupport()+", full "+gl.hasFullFBOSupport());
+ System.err.println("GL Profile: "+gl.getGLProfile());
+ System.err.println("GL Renderer Quirks:" + gl.getContext().getRendererQuirks().toString());
+ System.err.println("GL:" + gl + ", " + gl.getContext().getGLVersion());
+ }
gl.glEnable(GL.GL_DEPTH_TEST);
@@ -153,26 +159,38 @@ public class GearsES2 implements GLEventListener {
if(null == gear1) {
gear1 = new GearsObjectES2(st, 1.0f, 4.0f, 1.0f, 20, 0.7f, pmvMatrix, pmvMatrixUniform, colorU);
- System.err.println("gear1 created: "+gear1);
+ if(verbose) {
+ System.err.println("gear1 created: "+gear1);
+ }
} else {
gear1 = new GearsObjectES2(gear1, st, pmvMatrix, pmvMatrixUniform, colorU);
- System.err.println("gear1 reused: "+gear1);
+ if(verbose) {
+ System.err.println("gear1 reused: "+gear1);
+ }
}
if(null == gear2) {
gear2 = new GearsObjectES2(st, 0.5f, 2.0f, 2.0f, 10, 0.7f, pmvMatrix, pmvMatrixUniform, colorU);
- System.err.println("gear2 created: "+gear2);
+ if(verbose) {
+ System.err.println("gear2 created: "+gear2);
+ }
} else {
gear2 = new GearsObjectES2(gear2, st, pmvMatrix, pmvMatrixUniform, colorU);
- System.err.println("gear2 reused: "+gear2);
+ if(verbose) {
+ System.err.println("gear2 reused: "+gear2);
+ }
}
if(null == gear3) {
gear3 = new GearsObjectES2(st, 1.3f, 2.0f, 0.5f, 10, 0.7f, pmvMatrix, pmvMatrixUniform, colorU);
- System.err.println("gear3 created: "+gear3);
+ if(verbose) {
+ System.err.println("gear3 created: "+gear3);
+ }
} else {
gear3 = new GearsObjectES2(gear3, st, pmvMatrix, pmvMatrixUniform, colorU);
- System.err.println("gear3 reused: "+gear3);
+ if(verbose) {
+ System.err.println("gear3 reused: "+gear3);
+ }
}
final Object upstreamWidget = drawable.getUpstreamWidget();
@@ -264,22 +282,23 @@ public class GearsES2 implements GLEventListener {
gl.glEnable(GL.GL_CULL_FACE);
- if( ignoreFocus || hasFocus ) {
- gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
- } else {
- gl.glClearColor(0.2f, 0.2f, 0.2f, 0.0f);
- }
-
- // Special handling for the case where the GLJPanel is translucent
- // and wants to be composited with other Java 2D content
- if (GLProfile.isAWTAvailable() &&
- (drawable instanceof javax.media.opengl.awt.GLJPanel) &&
- !((javax.media.opengl.awt.GLJPanel) drawable).isOpaque() &&
- ((javax.media.opengl.awt.GLJPanel) drawable).shouldPreserveColorBufferIfTranslucent()) {
- gl.glClear(GL.GL_DEPTH_BUFFER_BIT);
- } else {
- gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
- }
+ if( clearBuffers ) {
+ if( ignoreFocus || hasFocus ) {
+ gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ } else {
+ gl.glClearColor(0.2f, 0.2f, 0.2f, 0.0f);
+ }
+ // Special handling for the case where the GLJPanel is translucent
+ // and wants to be composited with other Java 2D content
+ if (GLProfile.isAWTAvailable() &&
+ (drawable instanceof javax.media.opengl.awt.GLJPanel) &&
+ !((javax.media.opengl.awt.GLJPanel) drawable).isOpaque() &&
+ ((javax.media.opengl.awt.GLJPanel) drawable).shouldPreserveColorBufferIfTranslucent()) {
+ gl.glClear(GL.GL_DEPTH_BUFFER_BIT);
+ } else {
+ gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
+ }
+ }
st.useProgram(gl, true);
pmvMatrix.glPushMatrix();
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/RedSquareES2.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/RedSquareES2.java
index 6ff3dc3de..9b3fe74cd 100644
--- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/RedSquareES2.java
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/RedSquareES2.java
@@ -39,15 +39,16 @@ import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLUniformData;
public class RedSquareES2 implements GLEventListener {
- ShaderState st;
- PMVMatrix pmvMatrix;
- GLUniformData pmvMatrixUniform;
- GLArrayDataServer vertices ;
- GLArrayDataServer colors ;
- long t0;
+ private ShaderState st;
+ private PMVMatrix pmvMatrix;
+ private GLUniformData pmvMatrixUniform;
+ private GLArrayDataServer vertices ;
+ private GLArrayDataServer colors ;
+ private long t0;
private int swapInterval = 0;
- float aspect = 1.0f;
- boolean doRotate = true;
+ private float aspect = 1.0f;
+ private boolean doRotate = true;
+ private boolean clearBuffers = true;
public RedSquareES2(int swapInterval) {
this.swapInterval = swapInterval;
@@ -59,6 +60,7 @@ public class RedSquareES2 implements GLEventListener {
public void setAspect(float aspect) { this.aspect = aspect; }
public void setDoRotation(boolean rotate) { this.doRotate = rotate; }
+ public void setClearBuffers(boolean v) { clearBuffers = v; }
public void init(GLAutoDrawable glad) {
System.err.println(Thread.currentThread()+" RedSquareES2.init ...");
@@ -131,8 +133,10 @@ public class RedSquareES2 implements GLEventListener {
long t1 = System.currentTimeMillis();
GL2ES2 gl = glad.getGL().getGL2ES2();
- gl.glClearColor(0, 0, 0, 0);
- gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
+ if( clearBuffers ) {
+ gl.glClearColor(0, 0, 0, 0);
+ gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
+ }
st.useProgram(gl, true);
// One rotation every four seconds
pmvMatrix.glMatrixMode(PMVMatrix.GL_MODELVIEW);
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/awt/TestGearsES2AWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/awt/TestGearsES2AWT.java
index 0a8063297..9f62e4a49 100644
--- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/awt/TestGearsES2AWT.java
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/awt/TestGearsES2AWT.java
@@ -46,6 +46,7 @@ import com.jogamp.opengl.test.junit.util.QuitAdapter;
import java.awt.BorderLayout;
import java.awt.Dimension;
+import java.awt.EventQueue;
import java.awt.Frame;
import java.awt.TextArea;
import java.io.BufferedReader;
@@ -71,11 +72,22 @@ public class TestGearsES2AWT extends UITestCase {
static boolean shutdownDisposeFrame = true;
static boolean shutdownSystemExit = false;
static int swapInterval = 1;
+ static boolean exclusiveContext = false;
+ static Thread awtEDT;
@BeforeClass
public static void initClass() {
width = 640;
height = 480;
+ try {
+ EventQueue.invokeAndWait(new Runnable() {
+ public void run() {
+ awtEDT = Thread.currentThread();
+ } } );
+ } catch (Exception e) {
+ e.printStackTrace();
+ Assert.assertNull(e);
+ }
}
@AfterClass
@@ -106,6 +118,9 @@ public class TestGearsES2AWT extends UITestCase {
glCanvas.addGLEventListener(new GearsES2(swapInterval));
Animator animator = new Animator(glCanvas);
+ if( exclusiveContext ) {
+ animator.setExclusiveContext(awtEDT);
+ }
QuitAdapter quitAdapter = new QuitAdapter();
new AWTKeyAdapter(new TraceKeyAdapter(quitAdapter)).addTo(glCanvas);
@@ -117,8 +132,11 @@ public class TestGearsES2AWT extends UITestCase {
frame.setVisible(true);
}});
animator.start();
+ Assert.assertTrue(animator.isStarted());
+ Assert.assertTrue(animator.isAnimating());
+ Assert.assertEquals(exclusiveContext ? awtEDT : null, glCanvas.getExclusiveContextThread());
animator.setUpdateFPSFrames(60, System.err);
-
+
while(!quitAdapter.shouldQuit() /* && animator.isAnimating() */ && animator.getTotalFPSDuration()<duration) {
Thread.sleep(100);
}
@@ -127,8 +145,11 @@ public class TestGearsES2AWT extends UITestCase {
Assert.assertNotNull(glCanvas);
Assert.assertNotNull(animator);
+ Assert.assertEquals(exclusiveContext ? awtEDT : null, glCanvas.getExclusiveContextThread());
animator.stop();
- Assert.assertEquals(false, animator.isAnimating());
+ Assert.assertFalse(animator.isAnimating());
+ Assert.assertFalse(animator.isStarted());
+ Assert.assertEquals(null, glCanvas.getExclusiveContextThread());
frame.setVisible(false);
Assert.assertEquals(false, frame.isVisible());
javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
@@ -190,6 +211,8 @@ public class TestGearsES2AWT extends UITestCase {
} else if(args[i].equals("-vsync")) {
i++;
swapInterval = MiscUtils.atoi(args[i], swapInterval);
+ } else if(args[i].equals("-exclctx")) {
+ exclusiveContext = true;
} else if(args[i].equals("-layered")) {
shallUseOffscreenLayer = true;
} else if(args[i].equals("-layeredPBuffer")) {
@@ -216,6 +239,7 @@ public class TestGearsES2AWT extends UITestCase {
System.err.println("forceES2 "+forceES2);
System.err.println("forceGL3 "+forceGL3);
System.err.println("swapInterval "+swapInterval);
+ System.err.println("exclusiveContext "+exclusiveContext);
System.err.println("shallUseOffscreenLayer "+shallUseOffscreenLayer);
if(waitForKey) {
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java
index 86831a6fa..3910d1881 100644
--- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java
@@ -90,6 +90,7 @@ public class TestGearsES2NEWT extends UITestCase {
static boolean forceES2 = false;
static boolean forceGL3 = false;
static boolean mainRun = false;
+ static boolean exclusiveContext = false;
@BeforeClass
public static void initClass() {
@@ -143,9 +144,11 @@ public class TestGearsES2NEWT extends UITestCase {
});
}
- Animator animator = new Animator(glWindow);
+ Animator animator = new Animator();
+ animator.setModeBits(false, Animator.MODE_EXPECT_AWT_RENDERING_THREAD);
+ animator.setExclusiveContext(exclusiveContext);
+
QuitAdapter quitAdapter = new QuitAdapter();
-
//glWindow.addKeyListener(new TraceKeyAdapter(quitAdapter));
//glWindow.addWindowListener(new TraceWindowAdapter(quitAdapter));
glWindow.addKeyListener(quitAdapter);
@@ -165,62 +168,79 @@ public class TestGearsES2NEWT extends UITestCase {
if(e.getKeyChar()=='f') {
new Thread() {
public void run() {
+ final Thread t = glWindow.setExclusiveContextThread(null);
System.err.println("[set fullscreen pre]: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getWidth()+"x"+glWindow.getHeight()+", f "+glWindow.isFullscreen()+", a "+glWindow.isAlwaysOnTop()+", "+glWindow.getInsets());
glWindow.setFullscreen(!glWindow.isFullscreen());
System.err.println("[set fullscreen post]: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getWidth()+"x"+glWindow.getHeight()+", f "+glWindow.isFullscreen()+", a "+glWindow.isAlwaysOnTop()+", "+glWindow.getInsets());
+ glWindow.setExclusiveContextThread(t);
} }.start();
} else if(e.getKeyChar()=='a') {
new Thread() {
public void run() {
+ final Thread t = glWindow.setExclusiveContextThread(null);
System.err.println("[set alwaysontop pre]: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getWidth()+"x"+glWindow.getHeight()+", f "+glWindow.isFullscreen()+", a "+glWindow.isAlwaysOnTop()+", "+glWindow.getInsets());
glWindow.setAlwaysOnTop(!glWindow.isAlwaysOnTop());
System.err.println("[set alwaysontop post]: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getWidth()+"x"+glWindow.getHeight()+", f "+glWindow.isFullscreen()+", a "+glWindow.isAlwaysOnTop()+", "+glWindow.getInsets());
+ glWindow.setExclusiveContextThread(t);
} }.start();
} else if(e.getKeyChar()=='d') {
new Thread() {
public void run() {
+ final Thread t = glWindow.setExclusiveContextThread(null);
+ // while( null != glWindow.getExclusiveContextThread() ) ;
System.err.println("[set undecorated pre]: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getWidth()+"x"+glWindow.getHeight()+", d "+glWindow.isUndecorated()+", "+glWindow.getInsets());
glWindow.setUndecorated(!glWindow.isUndecorated());
System.err.println("[set undecorated post]: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getWidth()+"x"+glWindow.getHeight()+", d "+glWindow.isUndecorated()+", "+glWindow.getInsets());
+ glWindow.setExclusiveContextThread(t);
} }.start();
} else if(e.getKeyChar()=='s') {
new Thread() {
public void run() {
+ final Thread t = glWindow.setExclusiveContextThread(null);
System.err.println("[set position pre]: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getWidth()+"x"+glWindow.getHeight()+", "+glWindow.getInsets());
glWindow.setPosition(100, 100);
System.err.println("[set position post]: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getWidth()+"x"+glWindow.getHeight()+", "+glWindow.getInsets());
+ glWindow.setExclusiveContextThread(t);
} }.start();
} else if(e.getKeyChar()=='i') {
new Thread() {
public void run() {
+ final Thread t = glWindow.setExclusiveContextThread(null);
System.err.println("[set mouse visible pre]: "+glWindow.isPointerVisible());
glWindow.setPointerVisible(!glWindow.isPointerVisible());
System.err.println("[set mouse visible post]: "+glWindow.isPointerVisible());
+ glWindow.setExclusiveContextThread(t);
} }.start();
} else if(e.getKeyChar()=='j') {
new Thread() {
public void run() {
+ final Thread t = glWindow.setExclusiveContextThread(null);
System.err.println("[set mouse confined pre]: "+glWindow.isPointerConfined());
glWindow.confinePointer(!glWindow.isPointerConfined());
System.err.println("[set mouse confined post]: "+glWindow.isPointerConfined());
if(!glWindow.isPointerConfined()) {
demo.setConfinedFixedCenter(false);
}
+ glWindow.setExclusiveContextThread(t);
} }.start();
} else if(e.getKeyChar()=='J') {
new Thread() {
public void run() {
+ final Thread t = glWindow.setExclusiveContextThread(null);
System.err.println("[set mouse confined pre]: "+glWindow.isPointerConfined());
glWindow.confinePointer(!glWindow.isPointerConfined());
System.err.println("[set mouse confined post]: "+glWindow.isPointerConfined());
demo.setConfinedFixedCenter(glWindow.isPointerConfined());
+ glWindow.setExclusiveContextThread(t);
} }.start();
} else if(e.getKeyChar()=='w') {
new Thread() {
public void run() {
+ final Thread t = glWindow.setExclusiveContextThread(null);
System.err.println("[set mouse pos pre]");
glWindow.warpPointer(glWindow.getWidth()/2, glWindow.getHeight()/2);
System.err.println("[set mouse pos post]");
+ glWindow.setExclusiveContextThread(t);
} }.start();
}
}
@@ -232,24 +252,29 @@ public class TestGearsES2NEWT extends UITestCase {
}
});
+ animator.add(glWindow);
animator.start();
- // glWindow.setSkipContextReleaseThread(animator.getThread());
+ Assert.assertTrue(animator.isStarted());
+ Assert.assertTrue(animator.isAnimating());
+ Assert.assertEquals(exclusiveContext ? animator.getThread() : null, glWindow.getExclusiveContextThread());
glWindow.setVisible(true);
+ animator.setUpdateFPSFrames(60, showFPS ? System.err : null);
System.err.println("NW chosen: "+glWindow.getDelegatedWindow().getChosenCapabilities());
System.err.println("GL chosen: "+glWindow.getChosenCapabilities());
System.err.println("window pos/siz: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getWidth()+"x"+glWindow.getHeight()+", "+glWindow.getInsets());
- animator.setUpdateFPSFrames(60, showFPS ? System.err : null);
while(!quitAdapter.shouldQuit() && animator.isAnimating() && animator.getTotalFPSDuration()<duration) {
Thread.sleep(100);
}
+ Assert.assertEquals(exclusiveContext ? animator.getThread() : null, glWindow.getExclusiveContextThread());
animator.stop();
Assert.assertFalse(animator.isAnimating());
Assert.assertFalse(animator.isStarted());
+ Assert.assertEquals(null, glWindow.getExclusiveContextThread());
glWindow.destroy();
Assert.assertEquals(true, AWTRobotUtil.waitForRealized(glWindow, false));
}
@@ -316,6 +341,8 @@ public class TestGearsES2NEWT extends UITestCase {
} else if(args[i].equals("-vsync")) {
i++;
swapInterval = MiscUtils.atoi(args[i], swapInterval);
+ } else if(args[i].equals("-exclctx")) {
+ exclusiveContext = true;
} else if(args[i].equals("-es2")) {
forceES2 = true;
} else if(args[i].equals("-gl3")) {
@@ -365,14 +392,15 @@ public class TestGearsES2NEWT extends UITestCase {
System.err.println("undecorated "+undecorated);
System.err.println("atop "+alwaysOnTop);
System.err.println("fullscreen "+fullscreen);
- System.err.println("pmvDirect "+(!pmvUseBackingArray));
- System.err.println("swapInterval "+swapInterval);
+ System.err.println("pmvDirect "+(!pmvUseBackingArray));
System.err.println("mouseVisible "+mouseVisible);
System.err.println("mouseConfined "+mouseConfined);
System.err.println("loops "+loops);
System.err.println("loop shutdown "+loop_shutdown);
System.err.println("forceES2 "+forceES2);
System.err.println("forceGL3 "+forceGL3);
+ System.err.println("swapInterval "+swapInterval);
+ System.err.println("exclusiveContext "+exclusiveContext);
if(waitForKey) {
BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));