From f516d8cdc25577dd1227b85578d361749ab8063d Mon Sep 17 00:00:00 2001
From: Kenneth Russel Changes this GLDrawable to allow OpenGL rendering only from
@@ -129,7 +131,9 @@ public interface GLDrawable extends ComponentEvents {
drawable. Throws {@link GLException} if the rendering thread for
this drawable has been set and attempts are made to set or clear
the rendering thread from another thread, or if the passed
- thread is not equal to the current thread or null. setRenderingThread
on more than one drawable.
NOTE: Currently this routine is only advisory, which means that on some platforms the underlying optimizations are diff --git a/src/net/java/games/jogl/impl/GLContext.java b/src/net/java/games/jogl/impl/GLContext.java index 3923ed307..e0def092a 100644 --- a/src/net/java/games/jogl/impl/GLContext.java +++ b/src/net/java/games/jogl/impl/GLContext.java @@ -65,11 +65,6 @@ public abstract class GLContext { // the first event is received from the AWT event dispatch thread. private boolean realized; - // This avoids (as much as possible) losing the effects of - // setRenderingThread, which may need to be deferred if the - // component is not realized - private boolean deferredSetRenderingThread; - protected GLCapabilities capabilities; protected GLCapabilitiesChooser chooser; protected GL gl; @@ -83,6 +78,11 @@ public abstract class GLContext { protected Thread renderingThread; protected Runnable deferredReshapeAction; + // Error checking for setRenderingThread to ensure that one thread + // doesn't attempt to call setRenderingThread on more than one + // drawable + protected static final ThreadLocal perThreadRenderingContext = new ThreadLocal(); + // This is a workaround for a bug in NVidia's drivers where // vertex_array_range is only safe for single-threaded use; a bug // has been filed, ID 80174. When an Animator is created for a @@ -112,6 +112,14 @@ public abstract class GLContext { // Cache of the functions that are available to be called at the current // moment in time protected FunctionAvailabilityCache functionAvailability; + + // Support for recursive makeCurrent() calls as well as calling + // other drawables' display() methods from within another one's + protected static final ThreadLocal perThreadContextStack = new ThreadLocal() { + protected synchronized Object initialValue() { + return new GLContextStack(); + } + }; public GLContext(Component component, GLCapabilities capabilities, @@ -133,7 +141,9 @@ public abstract class GLContext { /** Runs the given runnable with this OpenGL context valid. */ public synchronized void invokeGL(Runnable runnable, boolean isReshape, Runnable initAction) throws GLException { - Thread currentThread = null; + // Could be more clever about not calling this every time, but + // Thread.currentThread() is very fast and this makes the logic simpler + Thread currentThread = Thread.currentThread(); // Defer JAWT and OpenGL operations until onscreen components are // realized @@ -144,7 +154,7 @@ public abstract class GLContext { if (!realized() || willSetRenderingThread || (renderingThread != null && - renderingThread != (currentThread = Thread.currentThread()))) { + renderingThread != currentThread)) { if (isReshape) { deferredReshapeAction = runnable; } @@ -157,8 +167,35 @@ public abstract class GLContext { return; } - if (renderingThread == null || deferredSetRenderingThread) { - deferredSetRenderingThread = false; + GLContextStack ctxStack = getPerThreadContextStack(); + GLContext curContext = ctxStack.peekContext(); + Runnable curInitAction = ctxStack.peekInitAction(); + boolean mustDoMakeCurrent = true; + boolean mustSkipFreeForRenderingThread = false; + boolean mustFreeBecauseOfNoRenderingThread = false; + + if (curContext == this) { + mustDoMakeCurrent = false; + } + + if (currentThread == renderingThread && curContext == null) { + mustSkipFreeForRenderingThread = true; + } + + if (!mustDoMakeCurrent && + renderingThread == null && + ctxStack.size() == 1) { + mustFreeBecauseOfNoRenderingThread = true; + } + + if (mustDoMakeCurrent) { + if (curContext != null) { + if (DEBUG) { + System.err.println("Freeing context " + curContext + " due to recursive makeCurrent"); + } + curContext.free(); + } + if (!makeCurrent(initAction)) { // Couldn't make the thread current because the component has not yet // been visualized, and therefore the context cannot be created. @@ -169,17 +206,23 @@ public abstract class GLContext { } return; } + if (DEBUG) { + System.err.println("Making context " + this + " current"); + } } + ctxStack.push(this, initAction); // At this point the OpenGL context is current. Offscreen contexts // handle resizing the backing bitmap in makeCurrent. Therefore we // may need to free and make the context current again if we // didn't actually make it current above. if (pendingOffscreenResize && renderingThread != null) { + ctxStack.pop(); free(); if (!makeCurrent(initAction)) { throw new GLException("Error while resizing offscreen context"); } + ctxStack.push(this, initAction); } boolean caughtException = false; @@ -199,8 +242,34 @@ public abstract class GLContext { // Disallow setRenderingThread if display action is throwing exceptions renderingThread = null; } - if (renderingThread == null) { + + if (!mustFreeBecauseOfNoRenderingThread && !mustSkipFreeForRenderingThread) { + ctxStack.pop(); + } + + // Free the context if another one was current, but not if the + // setRenderingThread optimization kicks in. However, if the + // setRenderingThread optimization has recently been disabled, + // must force a free. + if ((mustDoMakeCurrent && !mustSkipFreeForRenderingThread) || + mustFreeBecauseOfNoRenderingThread) { + if (mustFreeBecauseOfNoRenderingThread) { + // Must match previous push() + ctxStack.pop(); + } + + if (DEBUG) { + System.err.println("Freeing context " + this); + } + free(); + + if (curContext != null && !mustFreeBecauseOfNoRenderingThread) { + if (DEBUG) { + System.err.println("Making context " + curContext + " current again"); + } + curContext.makeCurrent(curInitAction); + } } } } @@ -240,18 +309,29 @@ public abstract class GLContext { if (renderingThread == null && currentThreadOrNull == null) { throw new GLException("Attempt to clear rendering thread when already cleared"); } + + Object currentThreadRenderingContext = perThreadRenderingContext.get(); + if (currentThreadOrNull != null && + currentThreadRenderingContext != null && + currentThreadRenderingContext != this) { + throw new GLException("Attempt to call setRenderingThread on more than one drawable in this thread"); + } + this.willSetRenderingThread = false; if (currentThreadOrNull == null) { renderingThread = null; - if (realized()) { - // We are guaranteed to own the context - free(); - } + perThreadRenderingContext.set(null); + // Just in case the end user wasn't planning on drawing the + // drawable even once more (which would give us a chance to free + // the context), try to free the context now by performing an + // invokeGL with a do-nothing action + invokeGL(new Runnable() { + public void run() { + } + }, false, initAction); } else { renderingThread = currentThreadOrNull; - if (realized()) { - makeCurrent(initAction); - } + perThreadRenderingContext.set(this); } } @@ -428,7 +508,7 @@ public abstract class GLContext { // set the current value of the proc address variable in the table object addressField.setLong(table, newProcAddress); if (DEBUG) { - System.err.println(glFuncName + " = 0x" + Long.toHexString(newProcAddress)); + // System.err.println(glFuncName + " = 0x" + Long.toHexString(newProcAddress)); } } catch (Exception e) { throw new GLException("Cannot get GL proc address for method \"" + @@ -446,6 +526,12 @@ public abstract class GLContext { textures between contexts. */ public abstract boolean isCreated(); + /** Support for recursive makeCurrent() calls as well as calling + other drawables' display() methods from within another one's */ + protected static GLContextStack getPerThreadContextStack() { + return (GLContextStack) perThreadContextStack.get(); + } + //---------------------------------------------------------------------- // Internals only below this point // diff --git a/src/net/java/games/jogl/impl/GLContextInitActionPair.java b/src/net/java/games/jogl/impl/GLContextInitActionPair.java new file mode 100755 index 000000000..ed2e78ac8 --- /dev/null +++ b/src/net/java/games/jogl/impl/GLContextInitActionPair.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. 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 + * MIDROSYSTEMS, 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 net.java.games.jogl.impl; + +public class GLContextInitActionPair { + private GLContext ctx; + private Runnable initAction; + + public GLContextInitActionPair(GLContext ctx, Runnable initAction) { + this.ctx = ctx; + this.initAction = initAction; + } + + public GLContext getContext() { + return ctx; + } + + public Runnable getInitAction() { + return initAction; + } +} diff --git a/src/net/java/games/jogl/impl/GLContextStack.java b/src/net/java/games/jogl/impl/GLContextStack.java new file mode 100755 index 000000000..dd80b0804 --- /dev/null +++ b/src/net/java/games/jogl/impl/GLContextStack.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. 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 + * MIDROSYSTEMS, 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 net.java.games.jogl.impl; + +import java.util.*; + +/** Implements a stack of GLContext objects along with the initActions + that need to be run if their creation is necessary. This is used + to detect redundant makeCurrent() calls and to allow one drawable + to call display() of another from within the first drawable's + display() method. */ + +public class GLContextStack { + private ArrayList data = new ArrayList(); + + /** Pushes this GLContext on the stack. The passed context must be non-null. */ + public void push(GLContext ctx, Runnable initAction) { + if (ctx == null) { + throw new IllegalArgumentException("Null contexts are not allowed here"); + } + + data.add(new GLContextInitActionPair(ctx, initAction)); + } + + /** Removes and returns the top GLContext and associated + initialization action, or null if there is none. */ + public GLContextInitActionPair pop() { + if (data.size() == 0) { + return null; + } + + return (GLContextInitActionPair) data.remove(data.size() - 1); + } + + /** Returns the top GLContext and associated initialization action + without removing it, or null if there is none. */ + public GLContextInitActionPair peek() { + return peek(0); + } + + /** Returns the ith GLContext and associated initialization + action from the top without removing it, or null if there is + none. */ + public GLContextInitActionPair peek(int i) { + if (data.size() - i <= 0) { + return null; + } + + return (GLContextInitActionPair) data.get(data.size() - i - 1); + } + + /** Returns the top GLContext without removing it, or null if there + is none. */ + public GLContext peekContext() { + return peekContext(0); + } + + /** Returns the ith GLContext from the top without removing + it, or null if there is none. */ + public GLContext peekContext(int i) { + GLContextInitActionPair pair = peek(i); + if (pair == null) { + return null; + } + + return pair.getContext(); + } + + /** Returns the top initialization action without removing it, or + null if there is none. */ + public Runnable peekInitAction() { + return peekInitAction(0); + } + + /** Returns the ith initialization action from the top + without removing it, or null if there is none. */ + public Runnable peekInitAction(int i) { + GLContextInitActionPair pair = peek(i); + if (pair == null) { + return null; + } + + return pair.getInitAction(); + } + + /** Returns the number of entries on the GLContext stack. */ + public int size() { + return data.size(); + } +} -- cgit v1.2.3