diff options
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 > <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)); |