aboutsummaryrefslogtreecommitdiffstats
path: root/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/jogl/classes/jogamp/opengl/GLDrawableHelper.java')
-rw-r--r--src/jogl/classes/jogamp/opengl/GLDrawableHelper.java386
1 files changed, 386 insertions, 0 deletions
diff --git a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java
new file mode 100644
index 000000000..4aae89bcf
--- /dev/null
+++ b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java
@@ -0,0 +1,386 @@
+/*
+ * 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
+ * met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution 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.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed or intended for use
+ * in the design, construction, operation or maintenance of any nuclear
+ * facility.
+ *
+ * Sun gratefully acknowledges that this software was originally authored
+ * and developed by Kenneth Bradley Russell and Christopher John Kline.
+ */
+
+package com.jogamp.opengl.impl;
+
+import java.util.*;
+import javax.media.opengl.*;
+
+/** Encapsulates the implementation of most of the GLAutoDrawable's
+ methods to be able to share it between GLCanvas and GLJPanel. */
+
+public class GLDrawableHelper {
+ protected static final boolean DEBUG = GLDrawableImpl.DEBUG;
+ private static final boolean VERBOSE = Debug.verbose();
+ private Object listenersLock = new Object();
+ private List listeners;
+ private volatile boolean listenersIter; // avoid java.util.ConcurrentModificationException
+ private Set listenersToBeInit;
+ private boolean autoSwapBufferMode;
+ private Object glRunnablesLock = new Object();
+ private ArrayList glRunnables;
+ private GLAnimatorControl animatorCtrl;
+
+ public GLDrawableHelper() {
+ reset();
+ }
+
+ public final void reset() {
+ synchronized(listenersLock) {
+ listeners = new ArrayList();
+ listenersIter = false;
+ listenersToBeInit = new HashSet();
+ }
+ autoSwapBufferMode = true;
+ synchronized(glRunnablesLock) {
+ glRunnables = new ArrayList();
+ }
+ animatorCtrl = null;
+ }
+
+ public final String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("GLAnimatorControl: "+animatorCtrl+", ");
+ synchronized(listenersLock) {
+ sb.append("GLEventListeners num "+listeners.size()+" [");
+ listenersIter = true;
+ for (int i=0; i < listeners.size(); i++) {
+ Object l = listeners.get(i);
+ sb.append(l);
+ sb.append("[init ");
+ sb.append( !listenersToBeInit.contains(l) );
+ sb.append("], ");
+ }
+ listenersIter = false;
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ public final void addGLEventListener(GLEventListener listener) {
+ addGLEventListener(-1, listener);
+ }
+
+ public final void addGLEventListener(int index, GLEventListener listener) {
+ synchronized(listenersLock) {
+ if(0>index) {
+ index = listeners.size();
+ }
+ // GLEventListener may be added after context is created,
+ // hence we earmark initialization for the next display call.
+ listenersToBeInit.add(listener);
+ if(!listenersIter) {
+ // fast path
+ listeners.add(index, listener);
+ } else {
+ // copy mode in case this is issued while iterating, eg via init, display, ..
+ List newListeners = (List) ((ArrayList) listeners).clone();
+ newListeners.add(index, listener);
+ listeners = newListeners;
+ }
+ }
+ }
+
+ public final void removeGLEventListener(GLEventListener listener) {
+ synchronized(listenersLock) {
+ if(!listenersIter) {
+ // fast path
+ listeners.remove(listener);
+ } else {
+ // copy mode in case this is issued while iterating, eg via init, display, ..
+ List newListeners = (List) ((ArrayList) listeners).clone();
+ newListeners.remove(listener);
+ listeners = newListeners;
+ }
+ listenersToBeInit.remove(listener);
+ }
+ }
+
+ /**
+ * Issues {@link javax.media.opengl.GLEventListener#dispose(javax.media.opengl.GLAutoDrawable)}
+ * to all listeners.
+ * @param drawable
+ */
+ public final void dispose(GLAutoDrawable drawable) {
+ synchronized(listenersLock) {
+ listenersIter = true;
+ for (int i=0; i < listeners.size(); i++) {
+ GLEventListener listener = (GLEventListener) listeners.get(i) ;
+ listener.dispose(drawable);
+ }
+ listenersIter = false;
+ }
+ }
+
+ private final boolean init(GLEventListener l, GLAutoDrawable drawable, boolean sendReshape) {
+ if(listenersToBeInit.remove(l)) {
+ l.init(drawable);
+ if(sendReshape) {
+ reshape(l, drawable, 0, 0, drawable.getWidth(), drawable.getHeight(), true /* setViewport */);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public final void init(GLAutoDrawable drawable) {
+ synchronized(listenersLock) {
+ listenersIter = true;
+ for (int i=0; i < listeners.size(); i++) {
+ GLEventListener listener = (GLEventListener) listeners.get(i) ;
+
+ // If make current ctx, invoked by invokGL(..), results in a new ctx, init gets called.
+ // This may happen not just for initial setup, but for ctx recreation due to resource change (drawable/window),
+ // hence the must always be initialized unconditional.
+ listenersToBeInit.add(listener);
+
+ if ( ! init( listener, drawable, false ) ) {
+ throw new GLException("GLEventListener "+listener+" already initialized: "+drawable);
+ }
+ }
+ listenersIter = false;
+ }
+ }
+
+ public final void display(GLAutoDrawable drawable) {
+ synchronized(listenersLock) {
+ listenersIter = true;
+ for (int i=0; i < listeners.size(); i++) {
+ GLEventListener listener = (GLEventListener) listeners.get(i) ;
+ // GLEventListener may need to be init,
+ // in case this one is added after the realization of the GLAutoDrawable
+ init( listener, drawable, true ) ;
+ listener.display(drawable);
+ }
+ listenersIter = false;
+ }
+ execGLRunnables(drawable);
+ }
+
+ private final void reshape(GLEventListener listener, GLAutoDrawable drawable,
+ int x, int y, int width, int height, boolean setViewport) {
+ if(setViewport) {
+ drawable.getGL().glViewport(x, y, width, height);
+ }
+ listener.reshape(drawable, x, y, width, height);
+ }
+
+ public final void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
+ synchronized(listenersLock) {
+ listenersIter = true;
+ for (int i=0; i < listeners.size(); i++) {
+ reshape((GLEventListener) listeners.get(i), drawable, x, y, width, height, 0==i);
+ }
+ listenersIter = false;
+ }
+ }
+
+ private final void execGLRunnables(GLAutoDrawable drawable) {
+ if(glRunnables.size()>0) {
+ // swap one-shot list asap
+ ArrayList _glRunnables = null;
+ synchronized(glRunnablesLock) {
+ if(glRunnables.size()>0) {
+ _glRunnables = glRunnables;
+ glRunnables = new ArrayList();
+ }
+ }
+ if(null!=_glRunnables) {
+ for (int i=0; i < _glRunnables.size(); i++) {
+ ((GLRunnable) _glRunnables.get(i)).run(drawable);
+ }
+ }
+ }
+ }
+
+ public final void setAnimator(GLAnimatorControl animator) throws GLException {
+ synchronized(glRunnablesLock) {
+ if(animatorCtrl!=animator && null!=animator && null!=animatorCtrl) {
+ throw new GLException("Trying to register GLAnimatorControl "+animator+", where "+animatorCtrl+" is already registered. Unregister first.");
+ }
+ animatorCtrl = animator;
+ }
+ }
+
+ public final GLAnimatorControl getAnimator() {
+ synchronized(glRunnablesLock) {
+ return animatorCtrl;
+ }
+ }
+
+ public final boolean isExternalAnimatorRunning() {
+ return ( null != animatorCtrl ) ? animatorCtrl.isStarted() && animatorCtrl.getThread() != Thread.currentThread() : false ;
+ }
+
+ public final boolean isExternalAnimatorAnimating() {
+ return ( null != animatorCtrl ) ? animatorCtrl.isAnimating() && animatorCtrl.getThread() != Thread.currentThread() : false ;
+ }
+
+ public final void invoke(GLAutoDrawable drawable, boolean wait, GLRunnable glRunnable) {
+ if( null == drawable || null == glRunnable ) {
+ return;
+ }
+ Throwable throwable = null;
+ GLRunnableTask rTask = null;
+ Object rTaskLock = new Object();
+ synchronized(rTaskLock) {
+ boolean deferred;
+ synchronized(glRunnablesLock) {
+ 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( !deferred ) {
+ drawable.display();
+ } else if( wait ) {
+ try {
+ 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);
+ }
+ }
+ }
+ }
+
+ public final void setAutoSwapBufferMode(boolean onOrOff) {
+ autoSwapBufferMode = onOrOff;
+ }
+
+ public final boolean getAutoSwapBufferMode() {
+ return autoSwapBufferMode;
+ }
+
+ private static final ThreadLocal perThreadInitAction = new ThreadLocal();
+
+ /** Principal helper method which runs a Runnable with the context
+ made current. This could have been made part of GLContext, but a
+ desired goal is to be able to implement GLAutoDrawable's in terms of
+ the GLContext's public APIs, and putting it into a separate
+ class helps ensure that we don't inadvertently use private
+ methods of the GLContext or its implementing classes.<br>
+ * <br>
+ * Remark: In case this method is called to dispose the GLDrawable/GLAutoDrawable,
+ * <code>initAction</code> shall be <code>null</code> to mark this cause.<br>
+ *
+ * @param drawable
+ * @param context
+ * @param runnable
+ * @param initAction
+ */
+ public final void invokeGL(GLDrawable drawable,
+ GLContext context,
+ Runnable runnable,
+ Runnable initAction) {
+ if(null==context) {
+ if (DEBUG) {
+ Exception e = new GLException(Thread.currentThread().getName()+" Info: GLDrawableHelper " + this + ".invokeGL(): NULL GLContext");
+ e.printStackTrace();
+ }
+ return;
+ }
+
+ if(null==initAction) {
+ // disposal case
+ if(!context.isCreated()) {
+ throw new GLException(Thread.currentThread().getName()+" GLDrawableHelper " + this + ".invokeGL(): Dispose case (no init action given): Native context is not created: "+context);
+ }
+ }
+
+ // Support for recursive makeCurrent() calls as well as calling
+ // other drawables' display() methods from within another one's
+ GLContext lastContext = GLContext.getCurrent();
+ Runnable lastInitAction = (Runnable) perThreadInitAction.get();
+ if (lastContext != null) {
+ lastContext.release();
+ }
+
+ int res = 0;
+ try {
+ res = context.makeCurrent();
+ if (res != GLContext.CONTEXT_NOT_CURRENT) {
+ if(null!=initAction) {
+ perThreadInitAction.set(initAction);
+ if (res == GLContext.CONTEXT_CURRENT_NEW) {
+ if (DEBUG) {
+ System.err.println("GLDrawableHelper " + this + ".invokeGL(): Running initAction");
+ }
+ initAction.run();
+ }
+ }
+ if(null!=runnable) {
+ if (DEBUG && VERBOSE) {
+ System.err.println("GLDrawableHelper " + this + ".invokeGL(): Running runnable");
+ }
+ runnable.run();
+ if (autoSwapBufferMode && null != initAction) {
+ if (drawable != null) {
+ drawable.swapBuffers();
+ }
+ }
+ }
+ }
+ } finally {
+ try {
+ if (res != GLContext.CONTEXT_NOT_CURRENT) {
+ context.release();
+ }
+ } catch (Exception e) {
+ }
+ if (lastContext != null) {
+ int res2 = lastContext.makeCurrent();
+ if (res2 == GLContext.CONTEXT_CURRENT_NEW) {
+ lastInitAction.run();
+ }
+ }
+ }
+ }
+
+}