JOGL v2.6.0-rc-20250706
JOGL, High-Performance Graphics Binding for Java™ (public API).
AnimatorBase.java
Go to the documentation of this file.
1/**
2 * Copyright 2010 JogAmp Community. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification, are
5 * permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 * conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 * of conditions and the following disclaimer in the documentation and/or other materials
12 * provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 *
24 * The views and conclusions contained in the software and documentation are those of the
25 * authors and should not be interpreted as representing official policies, either expressed
26 * or implied, of JogAmp Community.
27 */
28
29package com.jogamp.opengl.util;
30
31import jogamp.opengl.Debug;
32import jogamp.opengl.FPSCounterImpl;
33
34import java.io.PrintStream;
35import java.util.ArrayList;
36import java.util.Locale;
37
38import com.jogamp.opengl.GLAnimatorControl;
39import com.jogamp.opengl.GLAutoDrawable;
40import com.jogamp.opengl.GLException;
41import com.jogamp.opengl.GLProfile;
42
43import com.jogamp.common.ExceptionUtils;
44import com.jogamp.common.util.InterruptedRuntimeException;
45
46/**
47 * Base implementation of GLAnimatorControl<br>
48 * <p>
49 * The change synchronization is done via synchronized blocks on the AnimatorBase instance.<br>
50 * Status get / set activity is synced with a RecursiveLock, used as a memory barrier.<br>
51 * This is suitable, since all change requests are allowed to be expensive
52 * as they are not expected to be called at every frame.
53 * </p>
54 */
55public abstract class AnimatorBase implements GLAnimatorControl {
56 protected static final boolean DEBUG = Debug.debug("Animator");
57
58 /** A 1s timeout while waiting for a native action response, limiting {@link #finishLifecycleAction(Condition, long)} */
59 protected static final long TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION = 1000;
60
61 protected static final long POLLP_WAIT_FOR_FINISH_LIFECYCLE_ACTION = 32; // 2 frames @ 60Hz
62
63 /**
64 * If present in <code>modeBits</code> field and
65 * {@link GLProfile#isAWTAvailable() AWT is available},
66 * implementation is aware of the AWT EDT, otherwise not.
67 * <p>
68 * This is the <i>default</i>.
69 * </p>
70 * @see #setModeBits(boolean, int)
71 */
72 public static final int MODE_EXPECT_AWT_RENDERING_THREAD = 1 << 0;
73
74
75 @SuppressWarnings("serial")
76 public static class UncaughtAnimatorException extends RuntimeException {
77 final GLAutoDrawable drawable;
78 public UncaughtAnimatorException(final GLAutoDrawable drawable, final Throwable cause) {
79 super(cause);
80 this.drawable = drawable;
81 }
82 public final GLAutoDrawable getGLAutoDrawable() { return drawable; }
83 }
84
85 public static interface AnimatorImpl {
86 /**
87 * @param drawables
88 * @param ignoreExceptions
89 * @param printExceptions
90 * @throws UncaughtAnimatorException as caused by {@link GLAutoDrawable#display()}
91 */
92 void display(final ArrayList<GLAutoDrawable> drawables, final boolean ignoreExceptions, final boolean printExceptions) throws UncaughtAnimatorException;
93 boolean blockUntilDone(final Thread thread);
94 }
95
96 private static int seqInstanceNumber = 0;
97
98 protected int modeBits;
99 protected AnimatorImpl impl;
100 protected String baseName;
101
102 protected ArrayList<GLAutoDrawable> drawables = new ArrayList<GLAutoDrawable>();
103 protected boolean drawablesEmpty;
104 protected Thread animThread;
105 protected boolean ignoreExceptions;
106 protected boolean printExceptions;
107 protected boolean exclusiveContext;
110 protected FPSCounterImpl fpsCounter = new FPSCounterImpl();
111
112 private final static Class<?> awtAnimatorImplClazz;
113 static {
115 if( GLProfile.isAWTAvailable() ) {
116 Class<?> clazz;
117 try {
118 clazz = Class.forName("com.jogamp.opengl.util.AWTAnimatorImpl");
119 } catch (final Exception e) {
120 clazz = null;
121 }
122 awtAnimatorImplClazz = clazz;
123 } else {
124 awtAnimatorImplClazz = null;
125 }
126 }
127
128 /**
129 * Creates a new, empty Animator instance
130 * while expecting an AWT rendering thread if AWT is available.
131 *
132 * @see #AnimatorBase(int)
133 * @see GLProfile#isAWTAvailable()
134 * @see #setModeBits(boolean, int)
135 */
136 public AnimatorBase() {
137 this( MODE_EXPECT_AWT_RENDERING_THREAD ); // default!
138 }
139
140 /**
141 * Creates a new, empty Animator instance
142 * with given modeBits.
143 * <p>
144 * Passing {@link #MODE_EXPECT_AWT_RENDERING_THREAD} is considered default.
145 * However, passing {@code 0} is recommended if not using AWT in your application.
146 * </p>
147 *
148 * @see AnimatorBase#MODE_EXPECT_AWT_RENDERING_THREAD
149 * @see GLProfile#isAWTAvailable()
150 * @see #setModeBits(boolean, int)
151 */
152 public AnimatorBase(final int modeBits) {
153 this.modeBits = modeBits;
154 drawablesEmpty = true;
155 }
156
157 private static final boolean useAWTAnimatorImpl(final int modeBits) {
158 return 0 != ( MODE_EXPECT_AWT_RENDERING_THREAD & modeBits ) && null != awtAnimatorImplClazz;
159 }
160
161 /**
162 * Initializes implementation details post setup,
163 * invoked at {@link #add(GLAutoDrawable)}, {@link #start()}, ..
164 * <p>
165 * Operation is a NOP if <code>force</code> is <code>false</code>
166 * and this instance is already initialized.
167 * </p>
168 *
169 * @throws GLException if Animator is {@link #isStarted()}
170 */
171 protected final synchronized void initImpl(final boolean force) {
172 if( force || null == impl ) {
173 final String seqSuffix = String.format((Locale)null, "#%02d", seqInstanceNumber++);
174 if( useAWTAnimatorImpl( modeBits ) ) {
175 try {
176 impl = (AnimatorImpl) awtAnimatorImplClazz.newInstance();
177 baseName = getBaseName("AWT")+seqSuffix;
178 } catch (final Exception e) { e.printStackTrace(); }
179 }
180 if( null == impl ) {
181 impl = new DefaultAnimatorImpl();
182 baseName = getBaseName("")+seqSuffix;
183 }
184 if(DEBUG) {
185 System.err.println("Animator.initImpl: baseName "+baseName+", implClazz "+impl.getClass().getName()+" - "+toString()+" - "+getThreadName());
186 }
187 }
188 }
189 protected abstract String getBaseName(String prefix);
190
191 /**
192 * Enables or disables the given <code>bitValues</code>
193 * in this Animators <code>modeBits</code>.
194 * @param enable
195 * @param bitValues
196 *
197 * @throws GLException if Animator is {@link #isStarted()} and {@link #MODE_EXPECT_AWT_RENDERING_THREAD} about to change
198 * @see AnimatorBase#MODE_EXPECT_AWT_RENDERING_THREAD
199 */
200 public final synchronized void setModeBits(final boolean enable, final int bitValues) throws GLException {
201 final int _oldModeBits = modeBits;
202 if(enable) {
203 modeBits |= bitValues;
204 } else {
205 modeBits &= ~bitValues;
206 }
207 if( useAWTAnimatorImpl( _oldModeBits ) != useAWTAnimatorImpl( modeBits ) ) {
208 if( isStarted() ) {
209 throw new GLException("Animator already started");
210 }
211 initImpl(true);
212 }
213 }
214 public synchronized int getModeBits() { return modeBits; }
215
216
217 @Override
218 public final synchronized void add(final GLAutoDrawable drawable) {
219 if(DEBUG) {
220 System.err.println("Animator add: 0x"+Integer.toHexString(drawable.hashCode())+" - "+toString()+" - "+getThreadName());
221 }
222 if( drawables.contains(drawable) ) {
223 throw new IllegalArgumentException("Drawable already added to animator: "+this+", "+drawable);
224 }
225 initImpl(false);
226 pause();
227 if( isStarted() ) {
228 drawable.setExclusiveContextThread( exclusiveContext ? getExclusiveContextThread() : null ); // if already running ..
229 }
230 drawables.add(drawable);
231 drawablesEmpty = drawables.size() == 0;
232 drawable.setAnimator(this);
233 if( isPaused() ) { // either paused by pause() above, or if previously drawablesEmpty==true
234 resume();
235 }
236 final Condition waitForAnimatingAndECTCondition = new Condition() {
237 @Override
238 public boolean eval() {
239 final Thread dect = drawable.getExclusiveContextThread();
240 return isStarted() && !isPaused() && !isAnimating() && ( exclusiveContext && null == dect || !exclusiveContext && null != dect );
241 } };
242 final boolean res = finishLifecycleAction(waitForAnimatingAndECTCondition, 0);
243 if(DEBUG) {
244 System.err.println("Animator add: Wait for Animating/ECT OK: "+res+", "+toString()+", dect "+drawable.getExclusiveContextThread());
245 }
246 notifyAll();
247 }
248
249 @Override
250 public final synchronized void remove(final GLAutoDrawable drawable) {
251 if(DEBUG) {
252 System.err.println("Animator remove: 0x"+Integer.toHexString(drawable.hashCode())+" - "+toString()+" - "+getThreadName());
253 }
254 if( !drawables.contains(drawable) ) {
255 throw new IllegalArgumentException("Drawable not added to animator: "+this+", "+drawable);
256 }
257
258 if( exclusiveContext && isAnimating() ) {
259 drawable.setExclusiveContextThread( null );
260 final Condition waitForNullECTCondition = new Condition() {
261 @Override
262 public boolean eval() {
263 return null != drawable.getExclusiveContextThread();
264 } };
265 final boolean res = finishLifecycleAction(waitForNullECTCondition, POLLP_WAIT_FOR_FINISH_LIFECYCLE_ACTION);
266 if(DEBUG) {
267 System.err.println("Animator remove: Wait for Null-ECT OK: "+res+", "+toString()+", dect "+drawable.getExclusiveContextThread());
268 }
269 }
270
271 final boolean paused = pause();
272 drawables.remove(drawable);
273 drawablesEmpty = drawables.size() == 0;
274 drawable.setAnimator(null);
275 if(paused) {
276 resume();
277 }
278 final boolean res = finishLifecycleAction(waitForNotAnimatingIfEmptyCondition, 0);
279 if(DEBUG) {
280 System.err.println("Animator remove: Wait for !Animating-if-empty OK: "+res+", "+toString());
281 }
282 notifyAll();
283 }
284 private final Condition waitForNotAnimatingIfEmptyCondition = new Condition() {
285 @Override
286 public boolean eval() {
287 return isStarted() && drawablesEmpty && isAnimating();
288 } };
289
290
291 /**
292 * Dedicate all {@link GLAutoDrawable}'s context to the given exclusive context thread.
293 * <p>
294 * The given thread will be exclusive to all {@link GLAutoDrawable}'s context while {@link #isAnimating()}.
295 * </p>
296 * <p>
297 * If already started and disabling, method waits
298 * until change is propagated to all {@link GLAutoDrawable} if not
299 * called from the animator thread or {@link #getExclusiveContextThread() exclusive context thread}.
300 * </p>
301 * <p>
302 * Note: Utilizing this feature w/ AWT could lead to an AWT-EDT deadlock, depending on the AWT implementation.
303 * Hence it is advised not to use it with native AWT GLAutoDrawable like GLCanvas.
304 * </p>
305 *
306 * @param enable
307 * @return previous value
308 * @see #setExclusiveContext(boolean)
309 * @see #getExclusiveContextThread()
310 * @see #isExclusiveContextEnabled()
311 */
312 // @Override
313 public final synchronized Thread setExclusiveContext(final Thread t) {
314 final boolean enable = null != t;
315 final Thread old = userExclusiveContextThread;
316 if( enable && t != animThread ) { // disable: will be cleared at end after propagation && filter out own animThread usae
318 }
319 setExclusiveContext(enable);
320 return old;
321 }
322
323 /**
324 * Dedicate all {@link GLAutoDrawable}'s context to this animator thread.
325 * <p>
326 * The given thread will be exclusive to all {@link GLAutoDrawable}'s context while {@link #isAnimating()}.
327 * </p>
328 * <p>
329 * If already started and disabling, method waits
330 * until change is propagated to all {@link GLAutoDrawable} if not
331 * called from the animator thread or {@link #getExclusiveContextThread() exclusive context thread}.
332 * </p>
333 * <p>
334 * Note: Utilizing this feature w/ AWT could lead to an AWT-EDT deadlock, depending on the AWT implementation.
335 * Hence it is advised not to use it with native AWT GLAutoDrawable like GLCanvas.
336 * </p>
337 *
338 * @param enable
339 * @return previous value
340 * @see #setExclusiveContext(Thread)
341 * @see #getExclusiveContextThread()
342 * @see #isExclusiveContextEnabled()
343 */
344 // @Override
345 public final boolean setExclusiveContext(final boolean enable) {
346 final boolean propagateState;
347 final boolean oldExclusiveContext;
348 final Thread _exclusiveContextThread;
349 synchronized (AnimatorBase.this) {
350 propagateState = isStarted() && !drawablesEmpty;
351 _exclusiveContextThread = userExclusiveContextThread;
352 oldExclusiveContext = exclusiveContext;
353 exclusiveContext = enable;
354 if(DEBUG) {
355 System.err.println("AnimatorBase.setExclusiveContextThread: "+oldExclusiveContext+" -> "+exclusiveContext+", propagateState "+propagateState+", "+this);
356 }
357 }
358 final Thread dECT = enable ? ( null != _exclusiveContextThread ? _exclusiveContextThread : animThread ) : null ;
359 UncaughtAnimatorException displayCaught = null;
360 if( propagateState ) {
362 if( !enable ) {
363 if( Thread.currentThread() == getThread() || Thread.currentThread() == _exclusiveContextThread ) {
364 try {
365 display(); // propagate exclusive context -> off!
366 } catch (final UncaughtAnimatorException dre) {
367 displayCaught = dre;
368 }
369 } else {
370 final boolean resumed = isAnimating() ? false : resume();
371 int counter = 10;
372 while( 0<counter && isAnimating() && !validateDrawablesExclCtxState(dECT) ) {
373 try {
374 Thread.sleep(20);
375 } catch (final InterruptedException e) { }
376 counter--;
377 }
378 if(resumed) {
379 pause();
380 }
381 }
382 synchronized(AnimatorBase.this) {
384 }
385 }
386 }
387 if(DEBUG) {
388 System.err.println("AnimatorBase.setExclusiveContextThread: all-GLAD Ok: "+validateDrawablesExclCtxState(dECT)+", "+this);
389 if( null != displayCaught ) {
390 System.err.println("AnimatorBase.setExclusiveContextThread: caught: "+displayCaught.getMessage());
391 displayCaught.printStackTrace();
392 }
393 }
394 if( null != displayCaught ) {
395 throw displayCaught;
396 }
397 return oldExclusiveContext;
398 }
399
400 /**
401 * Returns <code>true</code>, if the exclusive context thread is enabled, otherwise <code>false</code>.
402 *
403 * @see #setExclusiveContext(boolean)
404 * @see #setExclusiveContext(Thread)
405 */
406 // @Override
407 public final synchronized boolean isExclusiveContextEnabled() {
408 return exclusiveContext;
409 }
410
411 /**
412 * Returns the exclusive context thread if {@link #isExclusiveContextEnabled()} and {@link #isStarted()}, otherwise <code>null</code>.
413 * <p>
414 * If exclusive context is enabled via {@link #setExclusiveContext(boolean)}
415 * the {@link #getThread() animator thread} is returned if above conditions are met.
416 * </p>
417 * <p>
418 * If exclusive context is enabled via {@link #setExclusiveContext(Thread)}
419 * the user passed thread is returned if above conditions are met.
420 * </p>
421 * @see #setExclusiveContext(boolean)
422 * @see #setExclusiveContext(Thread)
423 */
424 // @Override
425 public final synchronized Thread getExclusiveContextThread() {
427 }
428
429 /**
430 * Should be called at {@link #start()} and {@link #stop()}
431 * from within the animator thread.
432 * <p>
433 * At {@link #stop()} an additional {@link #display()} call shall be issued
434 * to allow propagation of releasing the exclusive thread.
435 * </p>
436 */
437 protected final synchronized void setDrawablesExclCtxState(final boolean enable) {
438 if(DEBUG) {
439 System.err.println("AnimatorBase.setExclusiveContextImpl exlusive "+exclusiveContext+": Enable "+enable+" for "+this+" - "+Thread.currentThread());
440 // Thread.dumpStack();
441 }
442 final Thread ect = getExclusiveContextThread();
443 for (int i=0; i<drawables.size(); i++) {
444 try {
445 drawables.get(i).setExclusiveContextThread( enable ? ect : null );
446 } catch (final RuntimeException e) {
447 e.printStackTrace();
448 }
449 }
450 }
451 protected final boolean validateDrawablesExclCtxState(final Thread expected) {
452 for (int i=0; i<drawables.size(); i++) {
453 if( expected != drawables.get(i).getExclusiveContextThread() ) {
454 return false;
455 }
456 }
457 return true;
458 }
459
460 @Override
461 public final synchronized Thread getThread() {
462 return animThread;
463 }
464
465 /** Called every frame to cause redrawing of all of the
466 GLAutoDrawables this Animator manages. Subclasses should call
467 this to get the most optimized painting behavior for the set of
468 components this Animator manages, in particular when multiple
469 lightweight widgets are continually being redrawn. */
470 protected final void display() throws UncaughtAnimatorException {
472 fpsCounter.tickFPS();
473 }
474
475 @Override
476 public final void setUpdateFPSFrames(final int frames, final PrintStream out) {
477 fpsCounter.setUpdateFPSFrames(frames, out);
478 }
479
480 @Override
481 public final void resetFPSCounter() {
482 fpsCounter.resetFPSCounter();
483 }
484
485 @Override
486 public final int getUpdateFPSFrames() {
487 return fpsCounter.getUpdateFPSFrames();
488 }
489
490 @Override
491 public final long getFPSStartTime() {
492 return fpsCounter.getFPSStartTime();
493 }
494
495 @Override
496 public final long getLastFPSUpdateTime() {
497 return fpsCounter.getLastFPSUpdateTime();
498 }
499
500 @Override
501 public final long getLastFPSPeriod() {
502 return fpsCounter.getLastFPSPeriod();
503 }
504
505 @Override
506 public final float getLastFPS() {
507 return fpsCounter.getLastFPS();
508 }
509
510 @Override
511 public final int getTotalFPSFrames() {
512 return fpsCounter.getTotalFPSFrames();
513 }
514
515 @Override
516 public final long getTotalFPSDuration() {
517 return fpsCounter.getTotalFPSDuration();
518 }
519
520 @Override
521 public final float getTotalFPS() {
522 return fpsCounter.getTotalFPS();
523 }
524
525 /** Sets a flag causing this Animator to ignore exceptions produced
526 while redrawing the drawables. By default this flag is set to
527 false, causing any exception thrown to halt the Animator. */
528 public final void setIgnoreExceptions(final boolean ignoreExceptions) {
529 this.ignoreExceptions = ignoreExceptions;
530 }
531
532 /** Sets a flag indicating that when exceptions are being ignored by
533 this Animator (see {@link #setIgnoreExceptions}), to print the
534 exceptions' stack traces for diagnostic information. Defaults to
535 false. */
536 public final void setPrintExceptions(final boolean printExceptions) {
537 this.printExceptions = printExceptions;
538 }
539
540 @Override
543 }
544
545 @Override
546 public final void setUncaughtExceptionHandler(final UncaughtExceptionHandler handler) {
547 uncaughtExceptionHandler = handler;
548 }
549
550 /**
551 * Should be called in case of an uncaught exception
552 * from within the animator thread, throws given exception if no handler has been installed.
553 * @return {@code true} if handled, otherwise {@code false}. In case of {@code false},
554 * caller needs to propagate the exception.
555 */
556 protected final synchronized boolean handleUncaughtException(final UncaughtAnimatorException ue) {
557 if( null != uncaughtExceptionHandler ) {
558 try {
560 } catch (final Throwable t) { /* ignore intentionally */ }
561 return true;
562 } else {
563 return false;
564 }
565 }
566
567 /**
568 * Should be called in case of an uncaught exception
569 * from within the animator thread to flush all animator.
570 * <p>
571 * The animator instance shall not be locked when calling this method!
572 * </p>
573 */
574 protected final void flushGLRunnables() {
575 for (int i=0; i<drawables.size(); i++) {
576 drawables.get(i).flushGLRunnables();
577 }
578 }
579
580 protected static interface Condition {
581 /**
582 * @return true if branching (continue waiting, action), otherwise false
583 */
584 boolean eval();
585 }
586
587 /**
588 * @param waitCondition method will wait until TO is reached or {@link Condition#eval() waitCondition.eval()} returns <code>false</code>.
589 * @param pollPeriod if <code>0</code>, method will wait until TO is reached or being notified.
590 * if &gt; <code>0</code>, method will wait for the given <code>pollPeriod</code> in milliseconds.
591 * @return <code>true</code> if {@link Condition#eval() waitCondition.eval()} returned <code>false</code>
592 * or if {@link AnimatorImpl#blockUntilDone(Thread) non-blocking}. Otherwise returns <code>false</code>.
593 */
594 protected final synchronized boolean finishLifecycleAction(final Condition waitCondition, long pollPeriod) {
595 /**
596 * It's hard to tell whether the thread which changes the lifecycle has
597 * dependencies on the Animator's internal thread. Currently we
598 * use a couple of heuristics to determine whether we should do
599 * the blocking wait().
600 */
601 initImpl(false);
602 final boolean blocking;
603 long remaining;
604 boolean nok;
605
607 blocking = true;
609 if( 0 >= pollPeriod ) {
610 pollPeriod = remaining;
611 }
612 nok = waitCondition.eval();
613 while ( nok && remaining>0 ) {
614 final long t1 = System.currentTimeMillis();
615 if( pollPeriod > remaining ) { pollPeriod = remaining; }
616 notifyAll();
617 try {
618 wait(pollPeriod);
619 } catch (final InterruptedException ie) {
620 throw new InterruptedRuntimeException(ie);
621 }
622 remaining -= System.currentTimeMillis() - t1 ;
623 nok = waitCondition.eval();
624 }
625 } else {
626 /**
627 * Even though we are not able to block until operation is completed at this point,
628 * best effort shall be made to preserve functionality.
629 * Here: Issue notifyAll() if waitCondition still holds and test again.
630 *
631 * Non blocking reason could be utilizing AWT Animator while operation is performed on AWT-EDT.
632 */
633 blocking = false;
634 remaining = 0;
635 nok = waitCondition.eval();
636 if( nok ) {
637 notifyAll();
638 nok = waitCondition.eval();
639 }
640 }
641 final boolean res = !nok || !blocking;
642 if(DEBUG || blocking && nok) { // Info only if DEBUG or ( blocking && not-ok ) ; !blocking possible if AWT
643 if( blocking && remaining<=0 && nok ) {
644 System.err.println("finishLifecycleAction(" + waitCondition.getClass().getName() + "): ++++++ timeout reached ++++++ " + getThreadName());
645 }
646 System.err.println("finishLifecycleAction(" + waitCondition.getClass().getName() + "): OK "+(!nok)+
647 "- pollPeriod "+pollPeriod+", blocking "+blocking+" -> res "+res+
648 ", waited " + (blocking ? ( TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION - remaining ) : 0 ) + "/" + TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION +
649 " - " + getThreadName());
650 System.err.println(" - "+toString());
651 if(nok) {
652 ExceptionUtils.dumpStack(System.err);
653 }
654 }
655 return res;
656 }
657
658 @Override
659 public synchronized boolean isStarted() {
660 return animThread != null ;
661 }
662
663 protected static String getThreadName() { return Thread.currentThread().getName(); }
664
665 @Override
666 public String toString() {
667 return getClass().getName()+"[started "+isStarted()+", animating "+isAnimating()+", paused "+isPaused()+", drawable "+drawables.size()+
668 ", totals[dt "+getTotalFPSDuration()+", frames "+getTotalFPSFrames()+", fps "+getTotalFPS()+
669 "], modeBits "+modeBits+", init'ed "+(null!=impl)+", animThread "+getThread()+", exclCtxThread "+exclusiveContext+"("+getExclusiveContextThread()+")]";
670 }
671}
A generic exception for OpenGL errors used throughout the binding as a substitute for RuntimeExceptio...
Specifies the the OpenGL profile.
Definition: GLProfile.java:77
static void initSingleton()
Static initialization of JOGL.
Definition: GLProfile.java:204
UncaughtAnimatorException(final GLAutoDrawable drawable, final Throwable cause)
Base implementation of GLAnimatorControl
AnimatorBase()
Creates a new, empty Animator instance while expecting an AWT rendering thread if AWT is available.
final void setUncaughtExceptionHandler(final UncaughtExceptionHandler handler)
Set the handler invoked when this animator abruptly stops due to an uncaught exception from one of it...
final synchronized boolean isExclusiveContextEnabled()
Returns true, if the exclusive context thread is enabled, otherwise false.
final boolean setExclusiveContext(final boolean enable)
Dedicate all GLAutoDrawable's context to this animator thread.
final synchronized void setDrawablesExclCtxState(final boolean enable)
Should be called at start() and stop() from within the animator thread.
AnimatorBase(final int modeBits)
Creates a new, empty Animator instance with given modeBits.
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.
static final long TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION
A 1s timeout while waiting for a native action response, limiting finishLifecycleAction(Condition,...
final void resetFPSCounter()
Reset all performance counter (startTime, currentTime, frame number)
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,...
abstract String getBaseName(String prefix)
ArrayList< GLAutoDrawable > drawables
UncaughtExceptionHandler uncaughtExceptionHandler
final synchronized void setModeBits(final boolean enable, final int bitValues)
Enables or disables the given bitValues in this Animators modeBits.
final UncaughtExceptionHandler getUncaughtExceptionHandler()
Returns the UncaughtExceptionHandler invoked when this animator abruptly stops due to an uncaught exc...
final long getFPSStartTime()
Returns the time of the first display call in milliseconds after enabling this feature via setUpdateF...
final long getLastFPSUpdateTime()
Returns the time of the last update interval in milliseconds, if this feature is enabled via setUpdat...
final synchronized boolean finishLifecycleAction(final Condition waitCondition, long pollPeriod)
final synchronized void initImpl(final boolean force)
Initializes implementation details post setup, invoked at add(GLAutoDrawable), start(),...
final synchronized Thread getThread()
final synchronized Thread getExclusiveContextThread()
Returns the exclusive context thread if isExclusiveContextEnabled() and isStarted(),...
final synchronized void add(final GLAutoDrawable drawable)
Adds a drawable to this animator's list of rendering drawables.
static final long POLLP_WAIT_FOR_FINISH_LIFECYCLE_ACTION
final synchronized Thread setExclusiveContext(final Thread t)
Dedicate all GLAutoDrawable's context to the given exclusive context thread.
final void setPrintExceptions(final boolean printExceptions)
Sets a flag indicating that when exceptions are being ignored by this Animator (see setIgnoreExceptio...
final void setIgnoreExceptions(final boolean ignoreExceptions)
Sets a flag causing this Animator to ignore exceptions produced while redrawing the drawables.
final void setUpdateFPSFrames(final int frames, final PrintStream out)
final boolean validateDrawablesExclCtxState(final Thread expected)
synchronized boolean isStarted()
Indicates whether this animator has been started.
A registered UncaughtExceptionHandler instance is invoked when an animator abruptly stops due to an u...
void uncaughtException(final GLAnimatorControl animator, final GLAutoDrawable drawable, final Throwable cause)
Method invoked when the given GLAnimatorControl is stopped due to the given uncaught exception happen...
An animator control interface, which implementation may drive a com.jogamp.opengl....
boolean isPaused()
Indicates whether this animator is started and either manually paused or paused automatically due to ...
boolean resume()
Resumes animation if paused.
boolean isAnimating()
Indicates whether this animator is started and is not paused.
boolean pause()
Pauses this animator.
A higher-level abstraction than GLDrawable which supplies an event based mechanism (GLEventListener) ...
Thread setExclusiveContextThread(Thread t)
Dedicates this instance's GLContext to the given thread.
abstract void setAnimator(GLAnimatorControl animatorControl)
Registers the usage of an animator, an com.jogamp.opengl.GLAnimatorControl implementation.
boolean blockUntilDone(final Thread thread)
void display(final ArrayList< GLAutoDrawable > drawables, final boolean ignoreExceptions, final boolean printExceptions)