summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xmake/scripts/tests-x64.bat40
-rwxr-xr-xmake/scripts/tests.sh15
-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
32 files changed, 2815 insertions, 492 deletions
diff --git a/make/scripts/tests-x64.bat b/make/scripts/tests-x64.bat
index 63a8d57f7..8367228b5 100755
--- a/make/scripts/tests-x64.bat
+++ b/make/scripts/tests-x64.bat
@@ -13,6 +13,27 @@ REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestSharedCon
REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.acore.TestGLProfile01NEWT %*
REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.acore.TestGLProfile02NEWT %*
REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.acore.TestGLContextDrawableSwitchNEWT %*
+REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestGLDebug00NEWT %*
+REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.acore.TestGLDebug01NEWT %*
+REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestGPUMemSec01NEWT %*
+REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.acore.TestGPUMemSec01NEWT %*
+REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestMapBuffer01NEWT
+REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestExclusiveContext01VSyncAnimNEWT %*
+REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestExclusiveContext01VSyncAnimAWT %*
+REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestExclusiveContext02FPSAnimNEWT %*
+REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestExclusiveContext02FPSAnimAWT %*
+scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestExclusiveContext11VSyncAnimNEWT %*
+REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestExclusiveContext12FPSAnimNEWT %*
+
+REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.acore.TestGLAutoDrawableDelegateOnOffscrnCapsNEWT $*
+REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestGLAutoDrawableGLWindowOnOffscrnCapsNEWT $*
+REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestGLAutoDrawableGLCanvasOnOffscrnCapsAWT $*
+REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestGLAutoDrawableFactoryOffscrnCapsNEWT $*
+REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestFBOAutoDrawableFactoryNEWT $*
+REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestFBOAutoDrawableOffThreadSharedContextES2NEWT $*
+REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestFBOMix2DemosES2NEWT $*
+REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestFBOMRTNEWT01 $*
+REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestFBOAutoDrawableDeadlockAWT $*
REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.awt.TestSwingAWT01GLn
REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.awt.TestGLCanvasAWTActionDeadlock00AWT %*
@@ -94,7 +115,7 @@ REM scripts\java-win64-dbg.bat testnoawt com.jogamp.opengl.test.junit.jogl.caps.
REM scripts\java-win64-dbg.bat testnoawt com.jogamp.opengl.test.junit.jogl.caps.TestMultisampleES2NEWT %*
REM scripts\java-win64-dbg.bat testawt com.jogamp.opengl.test.junit.jogl.caps.TestTranslucencyAWT %*
REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.caps.TestTranslucencyNEWT %*
-scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.parenting.TestTranslucentChildWindowBug632NEWT %*
+REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.parenting.TestTranslucentChildWindowBug632NEWT %*
REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.awt.TestBug461OffscreenSupersamplingSwingAWT
REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.glsl.TestShaderCompilationBug459AWT
@@ -122,23 +143,6 @@ REM scripts\java-win64.bat com.jogamp.opengl.test.junit.graph.demos.GPUTextNewtD
REM scripts\java-win64.bat com.jogamp.opengl.test.junit.graph.demos.GPURegionNewtDemo01
REM scripts\java-win64.bat com.jogamp.opengl.test.junit.graph.demos.GPURegionNewtDemo02
-REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestGLDebug00NEWT %*
-REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.acore.TestGLDebug01NEWT %*
-
-REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestGPUMemSec01NEWT %*
-REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.acore.TestGPUMemSec01NEWT %*
-REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestMapBuffer01NEWT
-
-REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.acore.TestGLAutoDrawableDelegateOnOffscrnCapsNEWT $*
-REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestGLAutoDrawableGLWindowOnOffscrnCapsNEWT $*
-REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestGLAutoDrawableGLCanvasOnOffscrnCapsAWT $*
-REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestGLAutoDrawableFactoryOffscrnCapsNEWT $*
-
-REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestFBOAutoDrawableFactoryNEWT $*
-REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestFBOAutoDrawableOffThreadSharedContextES2NEWT $*
-REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestFBOMix2DemosES2NEWT $*
-REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestFBOMRTNEWT01 $*
-REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestFBOAutoDrawableDeadlockAWT $*
REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.awt.TestBug461FBOSupersamplingSwingAWT
REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.glsl.TestRulerNEWT01
diff --git a/make/scripts/tests.sh b/make/scripts/tests.sh
index f253ed1e2..395546ab9 100755
--- a/make/scripts/tests.sh
+++ b/make/scripts/tests.sh
@@ -104,6 +104,7 @@ function jrun() {
#D_ARGS="-Djogamp.debug=all"
#D_ARGS="-Djogamp.debug=all -Dnativewindow.debug=all -Djogl.debug=all -Dnewt.debug=all"
#D_ARGS="-Dnativewindow.debug=all -Djogl.debug=all -Dnewt.debug=all"
+ #D_ARGS="-Djogl.debug.Animator"
#D_ARGS="-Djogl.debug=all -Dnewt.debug=all"
#D_ARGS="-Djogl.debug.GLDrawable -Djogl.debug.GLContext -Djogl.debug.GLCanvas"
#D_ARGS="-Djogl.debug.GLDrawable"
@@ -169,7 +170,6 @@ function jrun() {
#D_ARGS="-Dnewt.debug.Window -Dnativewindow.debug.JAWT -Djogl.debug.Animator"
#D_ARGS="-Dnewt.debug.Window"
#D_ARGS="-Xprof"
- #D_ARGS="-Djogl.debug.Animator"
#D_ARGS="-Dnativewindow.debug=all"
#D_ARGS="-Djogl.debug.GLCanvas"
#D_ARGS="-Djogl.debug.GLCanvas -Djogl.debug.Animator"
@@ -283,6 +283,14 @@ function testawtswt() {
#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLPointsNEWT $*
#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLMesaBug651NEWT $*
#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLMesaBug658NEWT $*
+#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestExclusiveContext01VSyncAnimNEWT $*
+#testawt com.jogamp.opengl.test.junit.jogl.acore.TestExclusiveContext01VSyncAnimAWT $*
+#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestExclusiveContext02FPSAnimNEWT $*
+#testawt com.jogamp.opengl.test.junit.jogl.acore.TestExclusiveContext02FPSAnimAWT $*
+testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestExclusiveContext11VSyncAnimNEWT $*
+#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestExclusiveContext12FPSAnimNEWT $*
+#testawt com.jogamp.opengl.test.junit.jogl.demos.es2.awt.TestGearsES2AWT $*
+#testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT $*
#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLAutoDrawableDelegateOnOffscrnCapsNEWT $*
#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLAutoDrawableFactoryOffscrnCapsNEWT $*
@@ -292,7 +300,6 @@ function testawtswt() {
#testawt com.jogamp.opengl.test.junit.jogl.acore.TestOffscreenLayer01GLCanvasAWT $*
#testawt com.jogamp.opengl.test.junit.jogl.acore.TestOffscreenLayer02NewtCanvasAWT $*
-#testawt com.jogamp.opengl.test.junit.jogl.demos.es2.awt.TestGearsES2AWT $*
#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestFBOAutoDrawableFactoryNEWT $*
#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestFBOOffThreadSharedContextMix2DemosES2NEWT $*
@@ -302,12 +309,12 @@ function testawtswt() {
#testawt com.jogamp.opengl.test.junit.jogl.acore.TestFBOAutoDrawableDeadlockAWT $*
#testawt com.jogamp.opengl.test.junit.jogl.awt.TestBug461FBOSupersamplingSwingAWT
-#testawt com.jogamp.opengl.test.junit.jogl.demos.es2.awt.TestGearsES2AWT $*
#testnoawt com.jogamp.opengl.test.junit.jogl.glu.TestGluUnprojectFloatNOUI $*
#testnoawt com.jogamp.opengl.test.junit.newt.TestRemoteWindow01NEWT $*
#testnoawt com.jogamp.opengl.test.junit.newt.TestRemoteGLWindows01NEWT $*
#testnoawt com.jogamp.opengl.test.junit.jogl.demos.gl2.newt.TestGearsNEWT $*
+#testnoawt com.jogamp.opengl.test.junit.jogl.demos.es1.newt.TestOlympicES1NEWT $*
#testnoawt com.jogamp.opengl.test.junit.jogl.demos.es1.newt.TestGearsES1NEWT $*
#testawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT $*
#testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT $*
@@ -455,7 +462,7 @@ function testawtswt() {
#testnoawt com.jogamp.opengl.test.junit.jogl.util.TestPNGImage01NEWT $*
#testawt com.jogamp.opengl.test.junit.jogl.util.texture.TestTexture01AWT
#testawt com.jogamp.opengl.test.junit.jogl.util.texture.TestPNGTextureFromFileAWT $*
-testnoawt com.jogamp.opengl.test.junit.jogl.util.texture.TestPNGTextureFromFileNEWT $*
+#testnoawt com.jogamp.opengl.test.junit.jogl.util.texture.TestPNGTextureFromFileNEWT $*
#testawt com.jogamp.opengl.test.junit.jogl.util.texture.TestGLReadBufferUtilTextureIOWrite01AWT $*
#testnoawt com.jogamp.opengl.test.junit.jogl.util.texture.TestGLReadBufferUtilTextureIOWrite01NEWT $*
#testawt com.jogamp.opengl.test.junit.jogl.util.texture.TestGLReadBufferUtilTextureIOWrite02AWT $*
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));