diff options
-rw-r--r-- | src/classes/com/sun/opengl/impl/GLDrawableHelper.java | 123 | ||||
-rw-r--r-- | src/classes/com/sun/opengl/impl/GLPbufferImpl.java | 20 | ||||
-rwxr-xr-x | src/classes/com/sun/opengl/impl/GLWorkerThread.java | 57 | ||||
-rw-r--r-- | src/classes/javax/media/opengl/GLCanvas.java | 29 | ||||
-rwxr-xr-x | src/classes/javax/media/opengl/Threading.java | 12 |
5 files changed, 202 insertions, 39 deletions
diff --git a/src/classes/com/sun/opengl/impl/GLDrawableHelper.java b/src/classes/com/sun/opengl/impl/GLDrawableHelper.java index 614b26096..920fac624 100644 --- a/src/classes/com/sun/opengl/impl/GLDrawableHelper.java +++ b/src/classes/com/sun/opengl/impl/GLDrawableHelper.java @@ -49,6 +49,7 @@ public class GLDrawableHelper { private volatile List listeners = new ArrayList(); private static final boolean DEBUG = Debug.debug("GLDrawableHelper"); private static final boolean VERBOSE = Debug.verbose(); + private static final boolean NVIDIA_CRASH_WORKAROUND = Debug.isPropertyDefined("jogl.nvidia.crash.workaround"); private boolean autoSwapBufferMode = true; public GLDrawableHelper() { @@ -104,46 +105,106 @@ public class GLDrawableHelper { GLContext context, Runnable runnable, Runnable initAction) { - // 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) { - perThreadInitAction.set(initAction); - if (res == GLContext.CONTEXT_CURRENT_NEW) { - if (DEBUG) { - System.err.println("GLDrawableHelper " + this + ".invokeGL(): Running initAction"); + if (GLWorkerThread.isStarted() && + GLWorkerThread.isWorkerThread()) { + // We're going to allow a context to be left current on the + // GLWorkerThread for optimization purposes + GLContext lastContext = GLContext.getCurrent(); + Runnable lastInitAction = (Runnable) perThreadInitAction.get(); + if (lastContext != null && lastContext != context) { + lastContext.release(); + } else { + lastContext = null; + } + + // FIXME: probably need to handle the case where the user is + // waiting for this context to be released; need to periodically + // release the context? See if anybody is waiting to make it + // current on another thread? (The latter would require the use + // of internal APIs...) + + int res = 0; + try { + res = context.makeCurrent(); + if (res != GLContext.CONTEXT_NOT_CURRENT) { + perThreadInitAction.set(initAction); + if (res == GLContext.CONTEXT_CURRENT_NEW) { + if (DEBUG) { + System.err.println("GLDrawableHelper " + this + ".invokeGL(): Running initAction"); + } + initAction.run(); + } + if (DEBUG && VERBOSE) { + System.err.println("GLDrawableHelper " + this + ".invokeGL(): Running runnable"); + } + runnable.run(); + if (autoSwapBufferMode) { + if (drawable != null) { + drawable.swapBuffers(); + } } - initAction.run(); } - if (DEBUG && VERBOSE) { - System.err.println("GLDrawableHelper " + this + ".invokeGL(): Running runnable"); + } finally { + + // FIXME: take this out as soon as possible + if (NVIDIA_CRASH_WORKAROUND) { + try { + if (res != GLContext.CONTEXT_NOT_CURRENT) { + context.release(); + } + } catch (Exception e) { + } } - runnable.run(); - if (autoSwapBufferMode) { - if (drawable != null) { - drawable.swapBuffers(); + + if (lastContext != null) { + int res2 = lastContext.makeCurrent(); + if (res2 == GLContext.CONTEXT_CURRENT_NEW) { + lastInitAction.run(); } } } - } finally { + } else { + // 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) { - context.release(); + perThreadInitAction.set(initAction); + if (res == GLContext.CONTEXT_CURRENT_NEW) { + if (DEBUG) { + System.err.println("GLDrawableHelper " + this + ".invokeGL(): Running initAction"); + } + initAction.run(); + } + if (DEBUG && VERBOSE) { + System.err.println("GLDrawableHelper " + this + ".invokeGL(): Running runnable"); + } + runnable.run(); + if (autoSwapBufferMode) { + if (drawable != null) { + drawable.swapBuffers(); + } + } } - } catch (Exception e) { - } - if (lastContext != null) { - int res2 = lastContext.makeCurrent(); - if (res2 == GLContext.CONTEXT_CURRENT_NEW) { - lastInitAction.run(); + } 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(); + } } } } diff --git a/src/classes/com/sun/opengl/impl/GLPbufferImpl.java b/src/classes/com/sun/opengl/impl/GLPbufferImpl.java index 196ea7223..feb9d3512 100644 --- a/src/classes/com/sun/opengl/impl/GLPbufferImpl.java +++ b/src/classes/com/sun/opengl/impl/GLPbufferImpl.java @@ -172,8 +172,12 @@ public class GLPbufferImpl implements GLPbuffer { PropertyChangeListener listener) {} public void destroy() { - context.destroy(); - pbufferDrawable.destroy(); + if (Threading.isSingleThreaded() && + !Threading.isOpenGLThread()) { + Threading.invokeOnOpenGLThread(destroyAction); + } else { + destroyAction.run(); + } } public int getFloatingPointMode() { @@ -238,4 +242,16 @@ public class GLPbufferImpl implements GLPbuffer { } private SwapBuffersOnEventDispatchThreadAction swapBuffersOnEventDispatchThreadAction = new SwapBuffersOnEventDispatchThreadAction(); + + class DestroyAction implements Runnable { + public void run() { + GLContext current = GLContext.getCurrent(); + if (current == context) { + context.release(); + } + context.destroy(); + pbufferDrawable.destroy(); + } + } + private DestroyAction destroyAction = new DestroyAction(); } diff --git a/src/classes/com/sun/opengl/impl/GLWorkerThread.java b/src/classes/com/sun/opengl/impl/GLWorkerThread.java index 52ada9bac..4091d1a77 100755 --- a/src/classes/com/sun/opengl/impl/GLWorkerThread.java +++ b/src/classes/com/sun/opengl/impl/GLWorkerThread.java @@ -40,6 +40,8 @@ package com.sun.opengl.impl; import java.lang.reflect.InvocationTargetException; +import java.security.*; +import javax.media.opengl.*; /** Singleton thread upon which all OpenGL work is performed by default. Unfortunately many vendors' OpenGL drivers are not really @@ -73,6 +75,7 @@ public class GLWorkerThread { lock = new Object(); thread = new Thread(new WorkerRunnable(), "JOGL GLWorkerThread"); + thread.setDaemon(true); started = true; synchronized (lock) { thread.start(); @@ -81,6 +84,59 @@ public class GLWorkerThread { } catch (InterruptedException e) { } } + + /* + + // Note: it appears that there is a bug in NVidia's current + // drivers where if a context was ever made current on a + // given thread and that thread has exited before program + // exit, a crash occurs in the drivers. Releasing the + // context from the given thread does not work around the + // problem. + // + // For the time being, we're going to work around this + // problem by not terminating the GLWorkerThread. In theory, + // shutting down the GLWorkerThread cleanly could be a good + // general solution to the problem of needing to + // cooperatively terminate all Animators at program exit. + // + // It appears that this doesn't even work around all of the + // kinds of crashes. Causing the context to be unilaterally + // released from the GLWorkerThread after each invocation + // seems to work around all of the kinds of crashes seen. + // + // These appear to be similar to the kinds of crashes seen + // when the Java2D/OpenGL pipeline terminates, and those are + // a known issue being fixed, so presumably these will be + // fixed in NVidia's next driver set. + + // Install shutdown hook to terminate daemon thread more or + // less cooperatively + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { + Object lockTemp = lock; + if (lockTemp == null) { + // Already terminating (?) + return; + } + synchronized (lockTemp) { + shouldTerminate = true; + lockTemp.notifyAll(); + try { + lockTemp.wait(500); + } catch (InterruptedException e) { + } + } + } + }); + return null; + } + }); + + */ + } else { throw new RuntimeException("Should not start GLWorkerThread twice"); } @@ -143,6 +199,7 @@ public class GLWorkerThread { } if (shouldTerminate) { + lock.notifyAll(); thread = null; lock = null; return; diff --git a/src/classes/javax/media/opengl/GLCanvas.java b/src/classes/javax/media/opengl/GLCanvas.java index 0329e4017..d555a1df0 100644 --- a/src/classes/javax/media/opengl/GLCanvas.java +++ b/src/classes/javax/media/opengl/GLCanvas.java @@ -166,11 +166,19 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { <B>Overrides:</B> <DL><DD><CODE>removeNotify</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */ public void removeNotify() { - context.destroy(); - drawable.setRealized(false); - super.removeNotify(); - if (DEBUG) { - System.err.println("GLCanvas.removeNotify()"); + try { + if (Threading.isSingleThreaded() && + !Threading.isOpenGLThread()) { + Threading.invokeOnOpenGLThread(destroyAction); + } else { + context.destroy(); + } + } finally { + drawable.setRealized(false); + super.removeNotify(); + if (DEBUG) { + System.err.println("GLCanvas.removeNotify()"); + } } } @@ -289,6 +297,17 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { private SwapBuffersOnEventDispatchThreadAction swapBuffersOnEventDispatchThreadAction = new SwapBuffersOnEventDispatchThreadAction(); + class DestroyAction implements Runnable { + public void run() { + GLContext current = GLContext.getCurrent(); + if (current == context) { + context.release(); + } + context.destroy(); + } + } + private DestroyAction destroyAction = new DestroyAction(); + // Disables the AWT's erasing of this Canvas's background on Windows // in Java SE 6. This internal API is not available in previous // releases, but the system property diff --git a/src/classes/javax/media/opengl/Threading.java b/src/classes/javax/media/opengl/Threading.java index 7aa57def5..f8d54d95d 100755 --- a/src/classes/javax/media/opengl/Threading.java +++ b/src/classes/javax/media/opengl/Threading.java @@ -197,7 +197,17 @@ public class Threading { return EventQueue.isDispatchThread(); } case WORKER: - return GLWorkerThread.isWorkerThread(); + if (Java2D.isOGLPipelineActive()) { + // FIXME: ideally only the QFT would be considered to be the + // "OpenGL thread", but we can not currently run all of JOGL's + // OpenGL work on that thread. For now, run the GLJPanel's + // Java2D/JOGL bridge on the QFT but everything else on the + // worker thread, except when we're already on the QFT. + return (Java2D.isQueueFlusherThread() || + GLWorkerThread.isWorkerThread()); + } else { + return GLWorkerThread.isWorkerThread(); + } default: throw new InternalError("Illegal single-threading mode " + mode); } |