JOGL v2.6.0-rc-20250712
JOGL, High-Performance Graphics Binding for Java™ (public API).
Animator.java
Go to the documentation of this file.
1/*
2 * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
3 * Copyright (c) 2010 JogAmp Community. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * - Redistribution of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * - Redistribution in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * Neither the name of Sun Microsystems, Inc. or the names of
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * This software is provided "AS IS," without a warranty of any kind. ALL
21 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
22 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
23 * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
24 * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
25 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
26 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
27 * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
28 * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
29 * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
30 * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
31 * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
32 *
33 * You acknowledge that this software is not designed or intended for use
34 * in the design, construction, operation or maintenance of any nuclear
35 * facility.
36 *
37 * Sun gratefully acknowledges that this software was originally authored
38 * and developed by Kenneth Bradley Russell and Christopher John Kline.
39 */
40
41package com.jogamp.opengl.util;
42
43import com.jogamp.common.ExceptionUtils;
44import com.jogamp.common.util.InterruptSource;
45import com.jogamp.common.util.SourcedInterruptedException;
46import com.jogamp.opengl.GLAutoDrawable;
47import com.jogamp.opengl.GLException;
48import com.jogamp.opengl.GLProfile;
49
50/** <P> An Animator can be attached to one or more {@link
51 GLAutoDrawable}s to drive their display() methods in a loop. </P>
52
53 <P> The Animator class creates a background thread in which the
54 calls to <code>display()</code> are performed. After each drawable
55 has been redrawn, a brief pause is performed to avoid swamping the
56 CPU, unless {@link #setRunAsFastAsPossible} has been called. </P>
57
58 * <p>
59 * The Animator execution thread does not run as a daemon thread,
60 * so it is able to keep an application from terminating.<br>
61 * Call {@link #stop() } to terminate the animation and it's execution thread.
62 * </p>
63 */
64public class Animator extends AnimatorBase {
65 private ThreadGroup threadGroup;
66 private Runnable runnable;
67 private boolean runAsFastAsPossible;
68 boolean isAnimating;
69 volatile boolean pauseIssued;
70 volatile boolean stopIssued;
71
72 /**
73 * Creates a new, empty Animator instance
74 * while expecting an AWT rendering thread if AWT is available.
75 *
76 * @see AnimatorBase#MODE_EXPECT_AWT_RENDERING_THREAD
77 * @see #Animator(int, ThreadGroup, GLAutoDrawable)
78 * @see GLProfile#isAWTAvailable()
79 * @see AnimatorBase#setModeBits(boolean, int)
80 */
81 public Animator() {
82 this(MODE_EXPECT_AWT_RENDERING_THREAD, null, null);
83 }
84
85 /**
86 * Creates a new, empty Animator instance
87 * with given modeBits.
88 * <p>
89 * Passing {@link AnimatorBase#MODE_EXPECT_AWT_RENDERING_THREAD} is considered default.
90 * However, passing {@code 0} is recommended if not using AWT in your application.
91 * </p>
92 *
93 * @see AnimatorBase#MODE_EXPECT_AWT_RENDERING_THREAD
94 * @see #Animator(int, ThreadGroup, GLAutoDrawable)
95 * @see GLProfile#isAWTAvailable()
96 * @see AnimatorBase#setModeBits(boolean, int)
97 */
98 public Animator(final int modeBits) {
99 this(modeBits, null, null);
100 }
101
102 /**
103 * Creates a new Animator w/ an associated ThreadGroup.
104 * <p>
105 * This ctor variant expects an AWT rendering thread if AWT is available.
106 * </p>
107 * @see AnimatorBase#MODE_EXPECT_AWT_RENDERING_THREAD
108 * @see #Animator(int, ThreadGroup, GLAutoDrawable)
109 * @see GLProfile#isAWTAvailable()
110 * @see AnimatorBase#setModeBits(boolean, int)
111 */
112 public Animator(final ThreadGroup tg) {
113 this(MODE_EXPECT_AWT_RENDERING_THREAD, tg, null);
114 }
115
116 /**
117 * Creates a new Animator for a particular drawable.
118 * <p>
119 * This ctor variant expects an AWT rendering thread if AWT is available.
120 * </p>
121 * @see AnimatorBase#MODE_EXPECT_AWT_RENDERING_THREAD
122 * @see #Animator(int, ThreadGroup, GLAutoDrawable)
123 * @see GLProfile#isAWTAvailable()
124 * @see AnimatorBase#setModeBits(boolean, int)
125 */
126 public Animator(final GLAutoDrawable drawable) {
127 this(MODE_EXPECT_AWT_RENDERING_THREAD, null, drawable);
128 }
129
130 /**
131 * Creates a new Animator w/ an associated ThreadGroup for a particular drawable.
132 * <p>
133 * This ctor variant expects an AWT rendering thread if AWT is available.
134 * </p>
135 * @see AnimatorBase#MODE_EXPECT_AWT_RENDERING_THREAD
136 * @see #Animator(int, ThreadGroup, GLAutoDrawable)
137 * @see GLProfile#isAWTAvailable()
138 * @see AnimatorBase#setModeBits(boolean, int)
139 */
140 public Animator(final ThreadGroup tg, final GLAutoDrawable drawable) {
141 this(MODE_EXPECT_AWT_RENDERING_THREAD, tg, drawable);
142 }
143
144 /**
145 * Creates a new Animator w/ an associated ThreadGroup for a particular drawable.
146 * <p>
147 * Passing {@link AnimatorBase#MODE_EXPECT_AWT_RENDERING_THREAD} is considered default.
148 * However, passing {@code 0} is recommended if not using AWT in your application.
149 * </p>
150 * @param modeBits pass {@link AnimatorBase#MODE_EXPECT_AWT_RENDERING_THREAD} if an AWT rendering thread is expected, otherwise {@code 0}.
151 * @param tg desired {@link ThreadGroup} or {@code null}
152 * @param drawable {@link #add(GLAutoDrawable) added} {@link GLAutoDrawable} or {@code null}
153 * @see AnimatorBase#MODE_EXPECT_AWT_RENDERING_THREAD
154 * @see GLProfile#isAWTAvailable()
155 * @see AnimatorBase#setModeBits(boolean, int)
156 */
157 public Animator(final int modeBits, final ThreadGroup tg, final GLAutoDrawable drawable) {
158 super(modeBits);
159 if( null != tg ) {
160 setThreadGroup(tg);
161 }
162 if( null != drawable ) {
163 add(drawable);
164 }
165 if(DEBUG) {
166 System.err.println("Animator created, modeBits 0x"+Integer.toHexString(modeBits)+", ThreadGroup: "+threadGroup+" and "+drawable);
167 }
168 }
169
170 @Override
171 protected final String getBaseName(final String prefix) {
172 return prefix + "Animator" ;
173 }
174
175 /**
176 * Sets a flag in this Animator indicating that it is to run as
177 * fast as possible. By default there is a brief pause in the
178 * animation loop which prevents the CPU from getting swamped.
179 * This method may not have an effect on subclasses.
180 */
181 public final synchronized void setRunAsFastAsPossible(final boolean runFast) {
182 runAsFastAsPossible = runFast;
183 }
184
185 class MainLoop implements Runnable {
186 @Override
187 public String toString() {
188 return "[started "+isStarted()+", animating "+isAnimating()+", paused "+isPaused()+", drawable "+drawables.size()+", drawablesEmpty "+drawablesEmpty+"]";
189 }
190
191 @Override
192 public void run() {
193 ThreadDeath caughtThreadDeath = null;
194 UncaughtAnimatorException caughtException = null;
195
196 try {
197 synchronized (Animator.this) {
198 if(DEBUG) {
199 System.err.println("Animator start on " + getThreadName() + ": " + toString());
200 }
201 fpsCounter.resetFPSCounter();
202 animThread = Thread.currentThread();
203 isAnimating = false;
204 // 'waitForStartedCondition' wake-up is handled below!
205 }
206
207 while (!stopIssued) {
208 synchronized (Animator.this) {
209 // Pause; Also don't consume CPU unless there is work to be done and not paused
210 boolean ectCleared = false;
211 while ( !stopIssued && ( pauseIssued || drawablesEmpty ) ) {
212 if( drawablesEmpty ) {
213 pauseIssued = true;
214 }
215 final boolean wasPaused = pauseIssued;
216 if (DEBUG) {
217 System.err.println("Animator pause on " + animThread.getName() + ": " + toString());
218 }
219 if ( exclusiveContext && !drawablesEmpty && !ectCleared ) {
220 ectCleared = true;
222 try {
223 display(); // propagate exclusive context -> off!
224 } catch (final UncaughtAnimatorException dre) {
225 caughtException = dre;
226 stopIssued = true;
227 break; // end pause loop
228 }
229 }
230 isAnimating = false;
231 Animator.this.notifyAll();
232 try {
233 Animator.this.wait();
234 } catch (final InterruptedException e) {
235 caughtException = new UncaughtAnimatorException(null, SourcedInterruptedException.wrap(e));
236 stopIssued = true;
237 break; // end pause loop
238 }
239 if (wasPaused) {
240 // resume from pause -> reset counter
241 fpsCounter.resetFPSCounter();
242 if (DEBUG) {
243 System.err.println("Animator resume on " + animThread.getName() + ": " + toString());
244 }
245 }
246 }
247 if (!stopIssued && !isAnimating) {
248 // Wakes up 'waitForStartedCondition' sync
249 // - and -
250 // Resume from pause or drawablesEmpty,
251 // implies !pauseIssued and !drawablesEmpty
252 isAnimating = true;
253 setDrawablesExclCtxState(exclusiveContext); // may re-enable exclusive context
254 Animator.this.notifyAll();
255 }
256 } // sync Animator.this
257 if ( !pauseIssued && !stopIssued ) {
258 try {
259 display();
260 } catch (final UncaughtAnimatorException dre) {
261 caughtException = dre;
262 stopIssued = true;
263 break; // end animation loop
264 }
265 if ( !runAsFastAsPossible ) {
266 // Avoid swamping the CPU
267 Thread.yield();
268 }
269 }
270 }
271 } catch(final ThreadDeath td) {
272 if(DEBUG) {
273 ExceptionUtils.dumpThrowable("", td);
274 }
275 caughtThreadDeath = td;
276 }
279 try {
280 display(); // propagate exclusive context -> off!
281 } catch (final UncaughtAnimatorException dre) {
282 if( null == caughtException ) {
283 caughtException = dre;
284 } else {
285 ExceptionUtils.dumpThrowable("(setExclusiveContextThread)", dre);
286 }
287 }
288 }
289 boolean flushGLRunnables = false;
290 boolean throwCaughtException = false;
291 synchronized (Animator.this) {
292 if(DEBUG) {
293 System.err.println("Animator stop on " + animThread.getName() + ": " + toString());
294 if( null != caughtException ) {
295 ExceptionUtils.dumpThrowable("", caughtException);
296 }
297 }
298 stopIssued = false;
299 pauseIssued = false;
300 isAnimating = false;
301 if( null != caughtException ) {
302 flushGLRunnables = true;
303 throwCaughtException = !handleUncaughtException(caughtException);
304 }
305 animThread = null;
306 Animator.this.notifyAll();
307 }
308 if( flushGLRunnables ) {
310 }
311 if( throwCaughtException ) {
312 throw caughtException;
313 }
314 if( null != caughtThreadDeath ) {
315 throw caughtThreadDeath;
316 }
317 }
318 }
319
320 @Override
321 public final synchronized boolean isAnimating() {
322 return animThread != null && isAnimating ;
323 }
324
325 @Override
326 public final synchronized boolean isPaused() {
327 return animThread != null && pauseIssued ;
328 }
329
330 /**
331 * Set a {@link ThreadGroup} for the {@link #getThread() animation thread}.
332 *
333 * @param tg the {@link ThreadGroup}
334 * @throws GLException if the animator has already been started
335 */
336 public final synchronized void setThreadGroup(final ThreadGroup tg) throws GLException {
337 if ( isStarted() ) {
338 throw new GLException("Animator already started.");
339 }
340 threadGroup = tg;
341 }
342
343 @Override
344 public final synchronized boolean start() {
345 if ( isStarted() ) {
346 return false;
347 }
348 if (runnable == null) {
349 runnable = new MainLoop();
350 }
351 fpsCounter.resetFPSCounter();
352 final Thread thread = new InterruptSource.Thread(threadGroup, runnable, getThreadName()+"-"+baseName);
353 thread.setDaemon(false); // force to be non daemon, regardless of parent thread
354 if(DEBUG) {
355 final Thread ct = Thread.currentThread();
356 System.err.println("Animator "+ct.getName()+"[daemon "+ct.isDaemon()+"]: starting "+thread.getName()+"[daemon "+thread.isDaemon()+"]");
357 }
358 thread.start();
359 return finishLifecycleAction(waitForStartedCondition, 0);
360 }
361 private final Condition waitForStartedCondition = new Condition() {
362 @Override
363 public boolean eval() {
364 return !isStarted() || (!drawablesEmpty && !isAnimating) ;
365 } };
366
367 @Override
368 public final synchronized boolean stop() {
369 if ( !isStarted() ) {
370 return false;
371 }
372 stopIssued = true;
373 return finishLifecycleAction(waitForStoppedCondition, 0);
374 }
375 private final Condition waitForStoppedCondition = new Condition() {
376 @Override
377 public boolean eval() {
378 return isStarted();
379 } };
380
381 @Override
382 public final synchronized boolean pause() {
383 if ( !isStarted() || pauseIssued ) {
384 return false;
385 }
386 pauseIssued = true;
387 return finishLifecycleAction(waitForPausedCondition, 0);
388 }
389 private final Condition waitForPausedCondition = new Condition() {
390 @Override
391 public boolean eval() {
392 // end waiting if stopped as well
393 return isStarted() && isAnimating;
394 } };
395
396 @Override
397 public final synchronized boolean resume() {
398 if ( !isStarted() || !pauseIssued ) {
399 return false;
400 }
401 pauseIssued = false;
402 return finishLifecycleAction(waitForResumeCondition, 0);
403 }
404 private final Condition waitForResumeCondition = new Condition() {
405 @Override
406 public boolean eval() {
407 // end waiting if stopped as well
408 return isStarted() && ( !drawablesEmpty && !isAnimating || drawablesEmpty && !pauseIssued ) ;
409 } };
410}
A generic exception for OpenGL errors used throughout the binding as a substitute for RuntimeExceptio...
Base implementation of GLAnimatorControl
final synchronized void setDrawablesExclCtxState(final boolean enable)
Should be called at start() and stop() from within the animator thread.
static final int MODE_EXPECT_AWT_RENDERING_THREAD
If present in modeBits field and AWT is available, implementation is aware of the AWT EDT,...
final void display()
Called every frame to cause redrawing of all of the GLAutoDrawables this Animator manages.
final void flushGLRunnables()
Should be called in case of an uncaught exception from within the animator thread to flush all animat...
final synchronized boolean handleUncaughtException(final UncaughtAnimatorException ue)
Should be called in case of an uncaught exception from within the animator thread,...
ArrayList< GLAutoDrawable > drawables
final synchronized boolean finishLifecycleAction(final Condition waitCondition, long pollPeriod)
final synchronized void add(final GLAutoDrawable drawable)
Adds a drawable to this animator's list of rendering drawables.
synchronized boolean isStarted()
Indicates whether this animator has been started.
Animator(final int modeBits)
Creates a new, empty Animator instance with given modeBits.
Definition: Animator.java:98
final synchronized boolean pause()
Pauses this animator.
Definition: Animator.java:382
final synchronized boolean resume()
Resumes animation if paused.
Definition: Animator.java:397
Animator(final ThreadGroup tg, final GLAutoDrawable drawable)
Creates a new Animator w/ an associated ThreadGroup for a particular drawable.
Definition: Animator.java:140
Animator()
Creates a new, empty Animator instance while expecting an AWT rendering thread if AWT is available.
Definition: Animator.java:81
final synchronized void setRunAsFastAsPossible(final boolean runFast)
Sets a flag in this Animator indicating that it is to run as fast as possible.
Definition: Animator.java:181
final synchronized boolean isAnimating()
Indicates whether this animator is started and is not paused.
Definition: Animator.java:321
Animator(final int modeBits, final ThreadGroup tg, final GLAutoDrawable drawable)
Creates a new Animator w/ an associated ThreadGroup for a particular drawable.
Definition: Animator.java:157
Animator(final GLAutoDrawable drawable)
Creates a new Animator for a particular drawable.
Definition: Animator.java:126
final synchronized void setThreadGroup(final ThreadGroup tg)
Set a ThreadGroup for the animation thread.
Definition: Animator.java:336
final synchronized boolean start()
Starts this animator, if not running.
Definition: Animator.java:344
final synchronized boolean isPaused()
Indicates whether this animator is started and either manually paused or paused automatically due to ...
Definition: Animator.java:326
final String getBaseName(final String prefix)
Definition: Animator.java:171
Animator(final ThreadGroup tg)
Creates a new Animator w/ an associated ThreadGroup.
Definition: Animator.java:112
final synchronized boolean stop()
Stops this animator.
Definition: Animator.java:368
A higher-level abstraction than GLDrawable which supplies an event based mechanism (GLEventListener) ...