diff options
13 files changed, 731 insertions, 292 deletions
diff --git a/src/jogl/classes/com/jogamp/opengl/impl/GLDrawableHelper.java b/src/jogl/classes/com/jogamp/opengl/impl/GLDrawableHelper.java index 1e954af1d..1596f0baf 100644 --- a/src/jogl/classes/com/jogamp/opengl/impl/GLDrawableHelper.java +++ b/src/jogl/classes/com/jogamp/opengl/impl/GLDrawableHelper.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 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 @@ -54,13 +55,14 @@ public class GLDrawableHelper { private boolean autoSwapBufferMode = true; private Object glRunnablesLock = new Object(); private ArrayList glRunnables = new ArrayList(); // one shot GL tasks - private Thread animatorThread = null; // default + private GLAnimatorControl animatorCtrl = null; // default public GLDrawableHelper() { } public String toString() { StringBuffer sb = new StringBuffer(); + sb.append("GLAnimatorControl: "+animatorCtrl+", "); synchronized(listenersLock) { sb.append("GLEventListeners num "+listeners.size()+" ["); for (Iterator iter = listeners.iterator(); iter.hasNext(); ) { @@ -176,51 +178,60 @@ public class GLDrawableHelper { } } - public void setAnimator(Thread animator) throws GLException { + public void setAnimator(GLAnimatorControl animator) throws GLException { synchronized(glRunnablesLock) { - if(animator!=animatorThread && null!=animator && null!=animatorThread) { - throw new GLException("Trying to register animator thread "+animator+", where "+animatorThread+" is already registered. Unregister first."); + if(animatorCtrl!=animator && null!=animator && null!=animatorCtrl) { + throw new GLException("Trying to register GLAnimatorControl "+animator+", where "+animatorCtrl+" is already registered. Unregister first."); } - animatorThread = animator; + animatorCtrl = animator; } } - public Thread getAnimator() { + public GLAnimatorControl getAnimator() { synchronized(glRunnablesLock) { - return animatorThread; + return animatorCtrl; } } + public final boolean isExternalAnimatorAnimating() { + return ( null != animatorCtrl ) ? animatorCtrl.isAnimating() && animatorCtrl.getThread() != Thread.currentThread() : false ; + } + public void invoke(GLAutoDrawable drawable, boolean wait, GLRunnable glRunnable) { if( null == drawable || null == glRunnable ) { return; } Throwable throwable = null; - Object lock = new Object(); GLRunnableTask rTask = null; - synchronized(lock) { - boolean callDisplay; + Object rTaskLock = new Object(); + synchronized(rTaskLock) { + boolean deferred; synchronized(glRunnablesLock) { - callDisplay = null == animatorThread || animatorThread == Thread.currentThread() ; - rTask = new GLRunnableTask(glRunnable, ( !callDisplay && wait ) ? lock : null); + deferred = isExternalAnimatorAnimating(); + if(!deferred) { + wait = false; // don't wait if exec immediatly + } + rTask = new GLRunnableTask(glRunnable, + wait ? rTaskLock : null, + wait /* catch Exceptions if waiting for result */); glRunnables.add(rTask); } - if( callDisplay ) { + if( !deferred ) { drawable.display(); } else if( wait ) { try { - lock.wait(); + rTaskLock.wait(); // free lock, allow execution of rTask } catch (InterruptedException ie) { throwable = ie; } + if(null==throwable) { + throwable = rTask.getThrowable(); + } + if(null!=throwable) { + throw new RuntimeException(throwable); + } } } - if(null==throwable) { - throwable = rTask.getThrowable(); - } - if(null!=throwable) { - throw new RuntimeException(throwable); - } } public void setAutoSwapBufferMode(boolean onOrOff) { diff --git a/src/jogl/classes/com/jogamp/opengl/impl/GLPbufferImpl.java b/src/jogl/classes/com/jogamp/opengl/impl/GLPbufferImpl.java index c2efb3f4e..dcfb2e3f5 100644 --- a/src/jogl/classes/com/jogamp/opengl/impl/GLPbufferImpl.java +++ b/src/jogl/classes/com/jogamp/opengl/impl/GLPbufferImpl.java @@ -138,11 +138,11 @@ public class GLPbufferImpl implements GLPbuffer { drawableHelper.removeGLEventListener(listener); } - public void setAnimator(Thread animator) { - drawableHelper.setAnimator(animator); + public void setAnimator(GLAnimatorControl animatorControl) { + drawableHelper.setAnimator(animatorControl); } - public Thread getAnimator() { + public GLAnimatorControl getAnimator() { return drawableHelper.getAnimator(); } diff --git a/src/jogl/classes/com/jogamp/opengl/impl/GLRunnableTask.java b/src/jogl/classes/com/jogamp/opengl/impl/GLRunnableTask.java index ea1e63b8b..a2a6939cd 100644 --- a/src/jogl/classes/com/jogamp/opengl/impl/GLRunnableTask.java +++ b/src/jogl/classes/com/jogamp/opengl/impl/GLRunnableTask.java @@ -38,27 +38,52 @@ import javax.media.opengl.GLAutoDrawable; public class GLRunnableTask implements GLRunnable { GLRunnable runnable; Object notifyObject; + boolean catchExceptions; + boolean isExecuted; Throwable runnableException; - public GLRunnableTask(GLRunnable runnable, Object notifyObject) { + public GLRunnableTask(GLRunnable runnable, Object notifyObject, boolean catchExceptions) { this.runnable = runnable ; this.notifyObject = notifyObject ; + this.catchExceptions = catchExceptions; + isExecuted = false; } public void run(GLAutoDrawable drawable) { - try { - runnable.run(drawable); - } catch (Throwable t) { - runnableException = t; - } - if(null != notifyObject) { + if(null == notifyObject) { + try { + runnable.run(drawable); + } catch (Throwable t) { + runnableException = t; + if(catchExceptions) { + runnableException.printStackTrace(); + } else { + throw new RuntimeException(runnableException); + } + } finally { + isExecuted=true; + } + } else { synchronized (notifyObject) { - notifyObject.notifyAll(); + try { + runnable.run(drawable); + } catch (Throwable t) { + runnableException = t; + if(catchExceptions) { + runnableException.printStackTrace(); + } else { + throw new RuntimeException(runnableException); + } + } finally { + isExecuted=true; + notifyObject.notifyAll(); + } } } } + public boolean isExecuted() { return isExecuted; } public Throwable getThrowable() { return runnableException; } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java b/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java index d8e1cf080..e3aff61c6 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java +++ b/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 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 @@ -51,11 +52,11 @@ class AWTAnimatorImpl extends AnimatorImpl { private Map repaintManagers = new IdentityHashMap(); private Map dirtyRegions = new IdentityHashMap(); - public void display(Animator animator, + public void display(AnimatorBase animator, boolean ignoreExceptions, boolean printExceptions) { Iterator iter = animator.drawableIterator(); - while (iter.hasNext()) { + while (animator.isAnimating() && !animator.getShouldStop() && !animator.getShouldPause() && iter.hasNext()) { GLAutoDrawable drawable = (GLAutoDrawable) iter.next(); if (drawable instanceof JComponent) { // Lightweight components need a more efficient drawing @@ -157,7 +158,7 @@ class AWTAnimatorImpl extends AnimatorImpl { } }; - public boolean skipWaitForStop(Thread thread) { + public boolean skipWaitForCompletion(Thread thread) { return ((Thread.currentThread() == thread) || EventQueue.isDispatchThread()); } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/Animator.java b/src/jogl/classes/com/jogamp/opengl/util/Animator.java index 66d3d5b88..5d48405e2 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/Animator.java +++ b/src/jogl/classes/com/jogamp/opengl/util/Animator.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 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 @@ -39,12 +40,16 @@ package com.jogamp.opengl.util; -import java.util.*; - -import javax.media.opengl.*; +import javax.media.opengl.GLAnimatorControl; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLException; +import javax.media.opengl.GLProfile; import com.jogamp.opengl.impl.Debug; +import java.util.*; + + /** <P> An Animator can be attached to one or more {@link GLAutoDrawable}s to drive their display() methods in a loop. </P> @@ -54,44 +59,34 @@ import com.jogamp.opengl.impl.Debug; CPU, unless {@link #setRunAsFastAsPossible} has been called. </P> */ -public class Animator { - protected static final boolean DEBUG = Debug.debug("Animator"); +public class Animator extends AnimatorBase { - private volatile ArrayList/*<GLAutoDrawable>*/ drawables = new ArrayList(); - private AnimatorImpl impl; + protected ThreadGroup threadGroup; private Runnable runnable; private boolean runAsFastAsPossible; - protected ThreadGroup threadGroup; - protected Thread thread; + protected volatile boolean isAnimating; + protected volatile boolean shouldPause; protected volatile boolean shouldStop; - protected boolean ignoreExceptions; - protected boolean printExceptions; - - /** Creates a new, empty Animator. */ - public Animator(ThreadGroup tg) { - if(GLProfile.isAWTAvailable()) { - try { - impl = (AnimatorImpl) Class.forName("com.jogamp.opengl.util.awt.AWTAnimatorImpl").newInstance(); - } catch (Exception e) { } - } - if(null==impl) { - impl = new AnimatorImpl(); + public Animator() { + super(); + if(DEBUG) { + System.err.println("Animator created"); } + } + + public Animator(ThreadGroup tg) { + super(); threadGroup = tg; if(DEBUG) { - System.out.println("Animator created, ThreadGroup: "+threadGroup); + System.err.println("Animator created, ThreadGroup: "+threadGroup); } } - public Animator() { - this((ThreadGroup)null); - } - /** Creates a new Animator for a particular drawable. */ public Animator(GLAutoDrawable drawable) { - this((ThreadGroup)null); + super(); add(drawable); } @@ -101,142 +96,131 @@ public class Animator { add(drawable); } - /** Adds a drawable to the list managed by this Animator. */ - public synchronized void add(GLAutoDrawable drawable) { - ArrayList newList = (ArrayList) drawables.clone(); - newList.add(drawable); - drawables = newList; - notifyAll(); - } - - /** Removes a drawable from the list managed by this Animator. */ - public synchronized void remove(GLAutoDrawable drawable) { - ArrayList newList = (ArrayList) drawables.clone(); - newList.remove(drawable); - drawables = newList; - } - - /** Returns an iterator over the drawables managed by this - Animator. */ - public Iterator/*<GLAutoDrawable>*/ drawableIterator() { - return drawables.iterator(); + protected String getBaseName(String prefix) { + return prefix + "Animator" ; } - /** 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. */ - public void setIgnoreExceptions(boolean ignoreExceptions) { - this.ignoreExceptions = ignoreExceptions; - } - - /** Sets a flag indicating that when exceptions are being ignored by - this Animator (see {@link #setIgnoreExceptions}), to print the - exceptions' stack traces for diagnostic information. Defaults to - false. */ - public void setPrintExceptions(boolean printExceptions) { - this.printExceptions = printExceptions; - } - - /** Sets a flag in this Animator indicating that it is to run as - fast as possible. By default there is a brief pause in the - animation loop which prevents the CPU from getting swamped. - This method may not have an effect on subclasses. */ + /** + * Sets a flag in this Animator indicating that it is to run as + * fast as possible. By default there is a brief pause in the + * animation loop which prevents the CPU from getting swamped. + * This method may not have an effect on subclasses. + */ public final void setRunAsFastAsPossible(boolean runFast) { runAsFastAsPossible = runFast; } - /** Called every frame to cause redrawing of all of the - GLAutoDrawables this Animator manages. Subclasses should call - this to get the most optimized painting behavior for the set of - components this Animator manages, in particular when multiple - lightweight widgets are continually being redrawn. */ - protected void display() { - impl.display(this, ignoreExceptions, printExceptions); - } - - private long startTime = 0; - private long curTime = 0; - private int totalFrames = 0; - class MainLoop implements Runnable { public void run() { try { if(DEBUG) { - System.out.println("Animator started: "+Thread.currentThread()); + System.err.println("Animator started: "+Thread.currentThread()); } startTime = System.currentTimeMillis(); curTime = startTime; + totalFrames = 0; + + synchronized (Animator.this) { + isAnimating = true; + Animator.this.notifyAll(); + } while (!shouldStop) { - // Don't consume CPU unless there is work to be done - if (drawables.size() == 0) { + // Don't consume CPU unless there is work to be done and not paused + if ( !shouldStop && ( shouldPause || drawables.size() == 0 ) ) { synchronized (Animator.this) { - while (drawables.size() == 0 && !shouldStop) { + boolean wasPaused = false; + while ( !shouldStop && ( shouldPause || drawables.size() == 0 ) ) { + if(DEBUG) { + System.err.println("Animator paused: "+Thread.currentThread()); + } + isAnimating = false; + wasPaused = true; + Animator.this.notifyAll(); try { Animator.this.wait(); } catch (InterruptedException e) { } } + if ( wasPaused ) { + startTime = System.currentTimeMillis(); + curTime = startTime; + totalFrames = 0; + if(DEBUG) { + System.err.println("Animator resumed: "+Thread.currentThread()); + } + isAnimating = true; + Animator.this.notifyAll(); + } } } - display(); - curTime = System.currentTimeMillis(); - totalFrames++; - if (!runAsFastAsPossible) { - // Avoid swamping the CPU - Thread.yield(); + if ( !shouldStop ) { + display(); + if (!runAsFastAsPossible) { + // Avoid swamping the CPU + Thread.yield(); + } } } - if(DEBUG) { - System.out.println("Animator stopped: "+Thread.currentThread()); - } } finally { - shouldStop = false; synchronized (Animator.this) { + if(DEBUG) { + System.err.println("Animator stopped: "+Thread.currentThread()); + } + shouldStop = false; + shouldPause = false; thread = null; - Animator.this.notify(); + isAnimating = false; + Animator.this.notifyAll(); } } } } - public long getStartTime() { return startTime; } - public long getCurrentTime() { return curTime; } - public long getDuration() { return curTime-startTime; } - public int getTotalFrames() { return totalFrames; } + public final synchronized boolean isStarted() { + return (thread != null); + } + + public final synchronized boolean isAnimating() { + return (thread != null) && isAnimating; + } + + public final synchronized boolean isPaused() { + return (thread != null) && shouldPause; + } - /** Starts this animator. */ public synchronized void start() { - if (thread != null) { - throw new GLException("Already started"); + boolean started = null != thread; + if ( started || isAnimating ) { + throw new GLException("Already running (started "+started+" (false), animating "+isAnimating+" (false))"); } if (runnable == null) { runnable = new MainLoop(); } + int id; + String threadName = Thread.currentThread().getName()+"-"+baseName; if(null==threadGroup) { - thread = new Thread(runnable); + thread = new Thread(runnable, threadName); } else { - thread = new Thread(threadGroup, runnable); - } - for(Iterator iter = drawables.iterator(); iter.hasNext(); ) { - ((GLAutoDrawable) iter.next()).setAnimator(thread); + thread = new Thread(threadGroup, runnable, threadName); } thread.start(); - } - /** Indicates whether this animator is currently running. This - should only be used as a heuristic to applications because in - some circumstances the Animator may be in the process of - shutting down and this method will still return true. */ - public synchronized boolean isAnimating() { - return (thread != null); + if (!impl.skipWaitForCompletion(thread)) { + while (!isAnimating && thread != null) { + try { + wait(); + } catch (InterruptedException ie) { + } + } + } } - /** Stops this animator. In most situations this method blocks until - completion, except when called from the animation thread itself - or in some cases from an implementation-internal thread like the - AWT event queue thread. */ public synchronized void stop() { + boolean started = null != thread; + if ( !started || !isAnimating ) { + throw new GLException("Not running (started "+started+" (true), animating "+isAnimating+" (true) )"); + } shouldStop = true; notifyAll(); @@ -244,16 +228,58 @@ public class Animator { // dependencies on the Animator's internal thread. Currently we // use a couple of heuristics to determine whether we should do // the blocking wait(). - if (!impl.skipWaitForStop(thread)) { - while (shouldStop && thread != null) { + if (!impl.skipWaitForCompletion(thread)) { + while (isAnimating && thread != null) { try { wait(); } catch (InterruptedException ie) { } } } - for(Iterator iter = drawables.iterator(); iter.hasNext(); ) { - ((GLAutoDrawable) iter.next()).setAnimator(null); + } + + public synchronized void pause() { + boolean started = null != thread; + if ( !started || !isAnimating || shouldPause ) { + throw new GLException("Invalid state (started "+started+" (true), animating "+isAnimating+" (true), paused "+shouldPause+" (false) )"); + } + shouldPause = true; + notifyAll(); + + if (!impl.skipWaitForCompletion(thread)) { + while (isAnimating && thread != null) { + try { + wait(); + } catch (InterruptedException ie) { + } + } + } + } + + public synchronized void resume() { + boolean started = null != thread; + if ( !started || !shouldPause ) { + throw new GLException("Invalid state (started "+started+" (true), paused "+shouldPause+" (true) )"); + } + shouldPause = false; + notifyAll(); + + if (!impl.skipWaitForCompletion(thread)) { + while (!isAnimating && thread != null) { + try { + wait(); + } catch (InterruptedException ie) { + } + } } } + + protected final boolean getShouldPause() { + return shouldPause; + } + + protected final boolean getShouldStop() { + return shouldStop; + } + } diff --git a/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java b/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java new file mode 100644 index 000000000..279fe33ef --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java @@ -0,0 +1,145 @@ +/** + * Copyright 2010 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.util; + +import com.jogamp.opengl.impl.Debug; +import java.util.ArrayList; +import java.util.Iterator; +import javax.media.opengl.GLAnimatorControl; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLProfile; + +/** + * Base implementation of GLAnimatorControl + */ +public abstract class AnimatorBase implements GLAnimatorControl { + protected static final boolean DEBUG = Debug.debug("Animator"); + + private static int animatorCount = 0; + + protected volatile ArrayList/*<GLAutoDrawable>*/ drawables = new ArrayList(); + protected AnimatorImpl impl; + protected String baseName; + protected Thread thread; + protected boolean ignoreExceptions; + protected boolean printExceptions; + protected long startTime = 0; + protected long curTime = 0; + protected int totalFrames = 0; + + /** Creates a new, empty Animator. */ + public AnimatorBase() { + if(GLProfile.isAWTAvailable()) { + try { + impl = (AnimatorImpl) Class.forName("com.jogamp.opengl.util.awt.AWTAnimatorImpl").newInstance(); + baseName = "AWTAnimator"; + } catch (Exception e) { } + } + if(null==impl) { + impl = new AnimatorImpl(); + baseName = "Animator"; + } + synchronized (Animator.class) { + animatorCount++; + baseName = baseName.concat("-"+animatorCount); + } + } + + protected abstract String getBaseName(String prefix); + + public synchronized void add(GLAutoDrawable drawable) { + ArrayList newList = (ArrayList) drawables.clone(); + newList.add(drawable); + drawables = newList; + drawable.setAnimator(this); + notifyAll(); + } + + public synchronized void remove(GLAutoDrawable drawable) { + ArrayList newList = (ArrayList) drawables.clone(); + newList.remove(drawable); + drawables = newList; + drawable.setAnimator(null); + notifyAll(); + } + + /** Called every frame to cause redrawing of all of the + GLAutoDrawables this Animator manages. Subclasses should call + this to get the most optimized painting behavior for the set of + components this Animator manages, in particular when multiple + lightweight widgets are continually being redrawn. */ + protected void display() { + impl.display(this, ignoreExceptions, printExceptions); + curTime = System.currentTimeMillis(); + totalFrames++; + } + + public Iterator drawableIterator() { + return drawables.iterator(); + } + + public long getCurrentTime() { + return curTime; + } + + public long getDuration() { + return curTime - startTime; + } + + protected abstract boolean getShouldPause(); + + protected abstract boolean getShouldStop(); + + public long getStartTime() { + return startTime; + } + + public int getTotalFrames() { + return totalFrames; + } + + public final synchronized Thread getThread() { + return thread; + } + + /** 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. */ + public void setIgnoreExceptions(boolean ignoreExceptions) { + this.ignoreExceptions = ignoreExceptions; + } + + /** Sets a flag indicating that when exceptions are being ignored by + this Animator (see {@link #setIgnoreExceptions}), to print the + exceptions' stack traces for diagnostic information. Defaults to + false. */ + public void setPrintExceptions(boolean printExceptions) { + this.printExceptions = printExceptions; + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/AnimatorImpl.java b/src/jogl/classes/com/jogamp/opengl/util/AnimatorImpl.java index 50b91b729..e4bf8d711 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/AnimatorImpl.java +++ b/src/jogl/classes/com/jogamp/opengl/util/AnimatorImpl.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 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 @@ -40,11 +41,11 @@ import javax.media.opengl.*; up this behavior if desired. */ class AnimatorImpl { - public void display(Animator animator, + public void display(AnimatorBase animator, boolean ignoreExceptions, boolean printExceptions) { Iterator iter = animator.drawableIterator(); - while (iter.hasNext()) { + while (animator.isAnimating() && !animator.getShouldStop() && !animator.getShouldPause() && iter.hasNext()) { GLAutoDrawable drawable = (GLAutoDrawable) iter.next(); try { drawable.display(); @@ -60,7 +61,7 @@ class AnimatorImpl { } } - public boolean skipWaitForStop(Thread thread) { + public boolean skipWaitForCompletion(Thread thread) { return (Thread.currentThread() == thread); } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java b/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java index 75c4cdff7..d098cf1a3 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java +++ b/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 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 @@ -36,88 +37,142 @@ * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ - package com.jogamp.opengl.util; import java.util.*; import javax.media.opengl.*; /** An Animator subclass which attempts to achieve a target - frames-per-second rate to avoid using all CPU time. The target FPS - is only an estimate and is not guaranteed. */ - -public class FPSAnimator extends Animator { - private Timer timer; - private int fps; - private boolean scheduleAtFixedRate; - - /** Creates an FPSAnimator with a given target frames-per-second - value. Equivalent to <code>FPSAnimator(null, fps)</code>. */ - public FPSAnimator(int fps) { - this(null, fps); - } - - /** Creates an FPSAnimator with a given target frames-per-second - value and a flag indicating whether to use fixed-rate - scheduling. Equivalent to <code>FPSAnimator(null, fps, - scheduleAtFixedRate)</code>. */ - public FPSAnimator(int fps, boolean scheduleAtFixedRate) { - this(null, fps, scheduleAtFixedRate); - } - - /** Creates an FPSAnimator with a given target frames-per-second - value and an initial drawable to animate. Equivalent to - <code>FPSAnimator(null, fps, false)</code>. */ - public FPSAnimator(GLAutoDrawable drawable, int fps) { - this(drawable, fps, false); - } - - /** Creates an FPSAnimator with a given target frames-per-second - value, an initial drawable to animate, and a flag indicating - whether to use fixed-rate scheduling. */ - public FPSAnimator(GLAutoDrawable drawable, int fps, boolean scheduleAtFixedRate) { - this.fps = fps; - if (drawable != null) { - add(drawable); +frames-per-second rate to avoid using all CPU time. The target FPS +is only an estimate and is not guaranteed. */ +public class FPSAnimator extends AnimatorBase { + private Timer timer = null; + private TimerTask task = null; + private int fps; + private boolean scheduleAtFixedRate; + private volatile boolean shouldRun; + + protected String getBaseName(String prefix) { + return "FPS" + prefix + "Animator" ; + } + + /** Creates an FPSAnimator with a given target frames-per-second + value. Equivalent to <code>FPSAnimator(null, fps)</code>. */ + public FPSAnimator(int fps) { + this(null, fps); + } + + /** Creates an FPSAnimator with a given target frames-per-second + value and a flag indicating whether to use fixed-rate + scheduling. Equivalent to <code>FPSAnimator(null, fps, + scheduleAtFixedRate)</code>. */ + public FPSAnimator(int fps, boolean scheduleAtFixedRate) { + this(null, fps, scheduleAtFixedRate); + } + + /** Creates an FPSAnimator with a given target frames-per-second + value and an initial drawable to animate. Equivalent to + <code>FPSAnimator(null, fps, false)</code>. */ + public FPSAnimator(GLAutoDrawable drawable, int fps) { + this(drawable, fps, false); + } + + /** Creates an FPSAnimator with a given target frames-per-second + value, an initial drawable to animate, and a flag indicating + whether to use fixed-rate scheduling. */ + public FPSAnimator(GLAutoDrawable drawable, int fps, boolean scheduleAtFixedRate) { + this.fps = fps; + if (drawable != null) { + add(drawable); + } + this.scheduleAtFixedRate = scheduleAtFixedRate; + } + + public long getStartTime() { return startTime; } + public long getCurrentTime() { return curTime; } + public long getDuration() { return curTime-startTime; } + public int getTotalFrames() { return totalFrames; } + + public final synchronized boolean isStarted() { + return (timer != null); + } + + public final synchronized boolean isAnimating() { + return (timer != null) && (task != null); + } + + public final synchronized boolean isPaused() { + return (timer != null) && (task == null); } - this.scheduleAtFixedRate = scheduleAtFixedRate; - } - /** Starts this FPSAnimator. */ - public synchronized void start() { - if (timer != null) { - throw new GLException("Already started"); + private void startTask() { + long delay = (long) (1000.0f / (float) fps); + task = new TimerTask() { + public void run() { + if(FPSAnimator.this.shouldRun) { + FPSAnimator.this.thread = Thread.currentThread(); + display(); + } + } + }; + + startTime = System.currentTimeMillis(); + curTime = startTime; + totalFrames = 0; + shouldRun = true; + + if (scheduleAtFixedRate) { + timer.scheduleAtFixedRate(task, 0, delay); + } else { + timer.schedule(task, 0, delay); + } } - timer = new Timer(); - long delay = (long) (1000.0f / (float) fps); - TimerTask task = new TimerTask() { - public void run() { - display(); + + public synchronized void start() { + if (timer != null) { + throw new GLException("Already started"); } - }; - if (scheduleAtFixedRate) { - timer.scheduleAtFixedRate(task, 0, delay); - } else { - timer.schedule(task, 0, delay); + timer = new Timer(); + startTask(); } - } - - /** Indicates whether this FPSAnimator is currently running. This - should only be used as a heuristic to applications because in - some circumstances the FPSAnimator may be in the process of - shutting down and this method will still return true. */ - public synchronized boolean isAnimating() { - return (timer != null); - } - - /** 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 void stop() { - if (timer == null) { - throw new GLException("Already stopped"); + + /** 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 void stop() { + if (timer == null) { + throw new GLException("Already stopped"); + } + shouldRun = false; + task.cancel(); + task = null; + timer.cancel(); + timer = null; + thread = null; + } + + public synchronized void pause() { + if (timer == null) { + throw new GLException("Not running"); + } + shouldRun = false; + task.cancel(); + task = null; + thread = null; + } + + public synchronized void resume() { + if (timer == null) { + throw new GLException("Not running"); + } + startTask(); + } + + protected final boolean getShouldPause() { + return (timer != null) && (task == null); + } + + protected final boolean getShouldStop() { + return (timer == null); } - timer.cancel(); - timer = null; - } } diff --git a/src/jogl/classes/javax/media/opengl/GLAnimatorControl.java b/src/jogl/classes/javax/media/opengl/GLAnimatorControl.java new file mode 100644 index 000000000..13788fe53 --- /dev/null +++ b/src/jogl/classes/javax/media/opengl/GLAnimatorControl.java @@ -0,0 +1,180 @@ +/** + * Copyright 2010 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 javax.media.opengl; + +/** + * An animator control interface, + * which implementation may drive a {@link javax.media.opengl.GLAutoDrawable} animation. + * <P> + * Note that the methods {@link #start()}, {@link #stop()}, {@link #pause()} and {@link #resume()} + * shall be implemented to fail-fast, ie {@link #start()} fails if not started, etc. + * This way an implementation can find implementation errors faster. + */ +public interface GLAnimatorControl { + + /** + * @return Time of the first display call in milliseconds. + * This value is reset if started or resumed. + * + * @see #start() + * @see #resume() + */ + long getStartTime(); + + /** + * @return Time of the last display call in milliseconds. + * This value is reset if started or resumed. + * + * @see #start() + * @see #resume() + */ + long getCurrentTime(); + + /** + * @return Duration <code>getCurrentTime() - getStartTime()</code>. + * + * @see #getStartTime() + * @see #getCurrentTime() + */ + long getDuration(); + + + /** + * @return Number of frames issued to all registered GLAutoDrawables registered + */ + /** + * @return Number of frame cycles displayed by all registered {@link javax.media.opengl.GLAutoDrawable} + * since the first display call, ie <code>getStartTime()</code>. + * This value is reset if started or resumed. + * + * @see #start() + * @see #resume() + */ + int getTotalFrames(); + + /** + * Indicates whether this animator is currently running, ie started. + * + * @see #start() + * @see #stop() + * @see #pause() + * @see #resume() + */ + boolean isStarted(); + + /** + * Indicates whether this animator is currently running and not paused. + * + * @see #start() + * @see #stop() + * @see #pause() + * @see #resume() + */ + boolean isAnimating(); + + /** + * Indicates whether this animator is currently running and paused. + * + * @see #start() + * @see #stop() + * @see #pause() + * @see #resume() + */ + boolean isPaused(); + + /** + * @return The animation thread if started, ie running. + * + * @see #start() + * @see #stop() + */ + Thread getThread(); + + /** + * Starts this animator, if not running. + * <P> + * In most situations this method blocks until + * completion, except when called from the animation thread itself + * or in some cases from an implementation-internal thread like the + * AWT event queue thread. + * <P> + * If started, all counters (time, frames, ..) are reset to zero. + * + * @see #stop() + * @see #isAnimating() + * @see #getThread() + * @throws GLException if started and animating already + */ + void start(); + + /** + * Stops this animator. + * <P> + * In most situations this method blocks until + * completion, except when called from the animation thread itself + * or in some cases from an implementation-internal thread like the + * AWT event queue thread. + * + * @see #start() + * @see #isAnimating() + * @see #getThread() + * @throws GLException if not started or not animating + */ + void stop(); + + /** + * Pauses this animator. + * <P> + * In most situations this method blocks until + * completion, except when called from the animation thread itself + * or in some cases from an implementation-internal thread like the + * AWT event queue thread. + * + * @see #resume() + * @see #isAnimating() + * @throws GLException if not started or not animating or already paused + */ + void pause(); + + /** + * Resumes animation if paused. + * <P> + * In most situations this method blocks until + * completion, except when called from the animation thread itself + * or in some cases from an implementation-internal thread like the + * AWT event queue thread. + * <P> + * If resumed, all counters (time, frames, ..) are reset to zero. + * + * @see #pause() + * @see #isAnimating() + * @throws GLException if not started or not paused + */ + void resume(); +} diff --git a/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java b/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java index 10a32fd1d..bc2f633ce 100644 --- a/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java +++ b/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 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 @@ -115,17 +116,6 @@ public interface GLAutoDrawable extends GLDrawable { * where you drag the window to another monitor. */ public static final boolean SCREEN_CHANGE_ACTION_ENABLED = Debug.getBooleanProperty("jogl.screenchange.action", true, AccessController.getContext()); - /** FIXME: - ** Invalid state, the resources are not yet ready to render. * - public static final int STATE_INVALID = 0; - - ** Valid state, all resources are ready to render, - and all registered {@link GLEventListener#init init(..)} are called. * - public static final int STATE_VALID = 1; - - ** Destroying state, currently executing the {@link #destroy()} method. * - public static final int STATE_DESTROYING = 2; */ - /** * Returns the context associated with this drawable. The returned * context will be synchronized. @@ -163,58 +153,55 @@ public interface GLAutoDrawable extends GLDrawable { /** * <p> - * Indicates whether a running animator thread is periodically issuing {@link #display()} calls or not.</p><br> + * 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> * <p> * This method shall be called by an animator implementation only,<br> - * e.g. {@link com.jogamp.opengl.util.Animator#start()}, passing the animator thread,<br> - * and {@link com.jogamp.opengl.util.Animator#stop()}, passing <code>null</code>.</p><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> * <p> * Impacts {@link #display()} and {@link #invoke(boolean, GLRunnable)} semantics.</p><br> * - * @param animator <code>null</code> reference indicates no running animator thread - * issues {@link #display()} calls on this <code>GLAutoDrawable</code>,<br> - * a valid reference indicates a running animator thread - * periodically issuing {@link #display()} calls. + * @param animator <code>null</code> reference indicates no animator is using + * this <code>GLAutoDrawable</code>,<br> + * a valid reference indicates an animator is using this <code>GLAutoDrawable</code>. * - * @throws GLException if a running animator thread is already registered and you try to register a different one without unregistering the previous one. + * @throws GLException if an animator is already registered. * @see #display() * @see #invoke(boolean, GLRunnable) + * @see javax.media.opengl.GLAnimatorControl */ - public void setAnimator(Thread animator) throws GLException; + public abstract void setAnimator(GLAnimatorControl animatorControl) throws GLException; /** - * @return the value of the registered animator thread + * @return the registered {@link javax.media.opengl.GLAnimatorControl} implementation, using this <code>GLAutoDrawable</code>. * - * @see #setAnimator(Thread) + * @see #setAnimator(javax.media.opengl.GLAnimatorControl) + * @see javax.media.opengl.GLAnimatorControl */ - public Thread getAnimator(); + public GLAnimatorControl getAnimator(); /** * <p> * Enqueues a one-shot {@link javax.media.opengl.GLRunnable}, - * which will be executed with the next {@link #display()} call.</p><br> + * which will be executed with the next {@link #display()} call.</p> * <p> - * If {@link #setAnimator(Thread)} has not registered no running animator thread, the default,<br> + * If a {@link javax.media.opengl.GLAnimatorControl} is registered, or if it's not animating, the default situation,<br> * or if the current thread is the animator thread,<br> - * a {@link #display()} call has to be issued after enqueueing the <code>GLRunnable</code>.<br> - * No extra synchronization must be performed in case <code>wait</code> is true, since it is executed in the current thread.</p><br> + * a {@link #display()} call has to be issued after enqueue the <code>GLRunnable</code>.<br> + * No extra synchronization must be performed in case <code>wait</code> is true, since it is executed in the current thread.</p> * <p> - * If {@link #setAnimator(Thread)} has registered a valid animator thread,<br> - * no call of {@link #display()} must be issued, since the animator thread performs it.<br> - * If <code>wait</code> is true, the implementation must wait until the <code>GLRunnable</code> is excecuted.</p><br> + * If {@link javax.media.opengl.GLAnimatorControl} is registered and is animating,<br> + * no call of {@link #display()} must be issued, since the animator thread will performs it.<br> + * If <code>wait</code> is true, the implementation must wait until the <code>GLRunnable</code> is executed.<br> + * </p><br> * - * @see #setAnimator(Thread) + * @see #setAnimator(javax.media.opengl.GLAnimatorControl) * @see #display() * @see javax.media.opengl.GLRunnable */ public void invoke(boolean wait, GLRunnable glRunnable); - /** FIXME: Returns the current state, - {@link #STATE_INVALID}, {@link #STATE_VALID} or {@link #STATE_DESTROYING}. - Tool to determine, e.g. if a {@link GLEventListener#dispose dispose(..)} - event is send in the context of the destruction of this GLAutoDrawable. - public int getCurrentState(); */ - /** Destroys all resources associated with this GLAutoDrawable, inclusive the GLContext. If a window is attached to it's implementation, it shall be closed. @@ -236,14 +223,13 @@ public interface GLAutoDrawable extends GLDrawable { * enqueued via {@link #invoke(boolean, GLRunnable)}.</li> * </ul></p> * <p> - * Called automatically by the - * window system toolkit upon receiving a repaint() request, - * except a running animator thread is registered with {@link #setAnimator(Thread)}.</p> <br> - * <p> - * Maybe called periodically by a running animator thread,<br> - * which must register itself with {@link #setAnimator(Thread)}.</p> <br> + * May be called periodically by a running {@link javax.media.opengl.GLAnimatorControl} implementation,<br> + * which must register itself with {@link #setAnimator(javax.media.opengl.GLAnimatorControl)}.</p> + * <p> + * Called automatically by the window system toolkit upon receiving a repaint() request, <br> + * except an {@link javax.media.opengl.GLAnimatorControl} implementation {@link javax.media.opengl.GLAnimatorControl#isAnimating()}.</p> * <p> - * This routine may be called manually for better control over the + * This routine may also be called manually for better control over the * rendering process. It is legal to call another GLAutoDrawable's * display method from within the {@link GLEventListener#display * display(..)} callback.</p> @@ -254,7 +240,7 @@ public interface GLAutoDrawable extends GLDrawable { * actual {@link GLEventListener#display display(..)} calls, * in case this has not been done yet.</p> * - * @see #setAnimator(Thread) + * @see #setAnimator(javax.media.opengl.GLAnimatorControl) */ public void display(); diff --git a/src/jogl/classes/javax/media/opengl/GLRunnable.java b/src/jogl/classes/javax/media/opengl/GLRunnable.java index 9d8345fb4..de0f5df48 100644 --- a/src/jogl/classes/javax/media/opengl/GLRunnable.java +++ b/src/jogl/classes/javax/media/opengl/GLRunnable.java @@ -28,16 +28,20 @@ package javax.media.opengl; -/** <p> Declares one-shot OpenGL commands, which client code can use to manage OpenGL - commands into a {@link GLAutoDrawable}. At the time any of these - methods is called, the drawable has made its associated OpenGL - context current, so it is valid to make OpenGL calls.<br></p> - <p> A GLRunnable maybe used to inject OpenGL commands via I/O event listener, - via {@link GLAutoDrawable#invoke(boolean, GLRunnable)}.</p> - */ +/** + * <p> + * Declares one-shot OpenGL commands usable for injection into a {@link GLAutoDrawable},<br> + * via {@link GLAutoDrawable#invoke(boolean, javax.media.opengl.GLRunnable)}.<br> + * {@link GLAutoDrawable} executes these commands within it's {@link GLAutoDrawable#display()} + * method while the OpenGL context is current.<br> + * <p> + * This might be useful to inject OpenGL commands from an I/O event listener. + */ public interface GLRunnable { - /** Called by the drawable to initiate one-shot OpenGL commands by the - client, like {@link GLEventListener#display(GLAutoDrawable)}. */ - public void run(GLAutoDrawable drawable); + /** + * Called by the drawable to initiate one-shot OpenGL commands by the + * client, like {@link GLEventListener#display(GLAutoDrawable)}. + */ + void run(GLAutoDrawable drawable); } diff --git a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java index f150b2507..705b12783 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 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 @@ -377,8 +378,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable { (int) ((getHeight() + bounds.getHeight()) / 2)); return; } - - if( null == getAnimator() ) { + if( this.drawableHelper.isExternalAnimatorAnimating() ) { display(); } } @@ -494,11 +494,11 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable { drawableHelper.removeGLEventListener(listener); } - public void setAnimator(Thread animator) { - drawableHelper.setAnimator(animator); + public void setAnimator(GLAnimatorControl animatorControl) { + drawableHelper.setAnimator(animatorControl); } - public Thread getAnimator() { + public GLAnimatorControl getAnimator() { return drawableHelper.getAnimator(); } diff --git a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java index d0d97fe31..72b782f9a 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 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 @@ -286,6 +287,10 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable { return; } + if ( drawableHelper.isExternalAnimatorAnimating() ) { + return; + } + if (backend == null || !isInitialized) { createAndInitializeBackend(); } @@ -380,11 +385,11 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable { drawableHelper.removeGLEventListener(listener); } - public void setAnimator(Thread animator) { - drawableHelper.setAnimator(animator); + public void setAnimator(GLAnimatorControl animatorControl) { + drawableHelper.setAnimator(animatorControl); } - public Thread getAnimator() { + public GLAnimatorControl getAnimator() { return drawableHelper.getAnimator(); } |