From 36887db6c983244917802f3ec761a0d28171887a Mon Sep 17 00:00:00 2001 From: Kenneth Russel Date: Tue, 21 Feb 2006 09:43:43 +0000 Subject: Added optimized path to GLDrawableHelper for situation where GLWorkerThread is being used; last context made current on that thread is left current on that thread. In the case where only a single OpenGL context is in use this eliminates the repeated calls to makeCurrent. Ran into same NVidia driver bug causing crashes upon exit with Java2D/OpenGL pipeline. Added workaround to GLDrawableHelper which can be enabled with -Djogl.nvidia.crash.workaround, which just disables this optimization. Fixed GLCanvas and GLPbufferImpl's destruction paths to behave correctly in the face of the context being left current on the GLWorkerThread. Updated code in Threading related to GLWorkerThread to interoperate better with Java2D/OpenGL pipeline. git-svn-id: file:///usr/local/projects/SUN/JOGL/git-svn/svn-server-sync/jogl/trunk@629 232f8b59-042b-4e1e-8c03-345bb8c30851 --- .../com/sun/opengl/impl/GLDrawableHelper.java | 123 +++++++++++++++------ src/classes/com/sun/opengl/impl/GLPbufferImpl.java | 20 +++- .../com/sun/opengl/impl/GLWorkerThread.java | 57 ++++++++++ src/classes/javax/media/opengl/GLCanvas.java | 29 ++++- src/classes/javax/media/opengl/Threading.java | 12 +- 5 files changed, 202 insertions(+), 39 deletions(-) (limited to 'src') 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 { Overrides:
removeNotify in class java.awt.Component
*/ 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); } -- cgit v1.2.3