diff options
author | Sven Gothel <[email protected]> | 2012-01-08 06:31:17 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2012-01-08 06:31:17 +0100 |
commit | 098398c2a9145447da5314eed9792b3738c2d515 (patch) | |
tree | ec8743137418e6b92644ba02f21c73f559145b16 | |
parent | 7ce29d85bb85c003c9dc3b94efa84b55dfbb7f86 (diff) |
GLContext*/GLDrawableHelper: Fix consistency of recursive makeCurrent()/release()/destroy() calls ; Enable context switch tracing ; GLCanvas: proper AbstractGraphicsDevice destruction
GLContext*/GLDrawableHelper: Fix consistency of recursive makeCurrent()/release()/destroy() calls
Utilizing volatile and lock.tryLock(0) for lockConsiderFailFast(),
reducing redundant synchronization and using RecursiveLock implicit sync.
GLContext 'early-out' is the case where the thread already holds the
context, ie. context is already current and the native makeCurrent is skipped.
makeCurrent()'s 'early-out' w/o incr. the recursive lock of GLContext
and it's NativeSurface could lead to asymetry in lock/unlock count
with release()/destroy() calls. The 1st release actually released the
native ctx already.
Properly utilize recursive lock/unlock in all cases and impl. 'early-out' after locking.
Following the above in GLDrawableHelper.invokeGL()'s 'early-out' case as well,
ie calling makeCurrent()/release() symmetrical.
Introduce GLDrawableHelper.disposeGL(), which issues dispose on all GLEventListeners
within a current context and issued context destruction directly.
This simplifies GLAutodrawable's destroy/dispose calls and ensures
that the above sequence of events happens atomically (lock is being hold until destruction).
Enable context switch tracing
If property 'jogl.debug.GLContext.TraceSwitch' is defined, trace context switch.
GLCanvas: proper AbstractGraphicsDevice destruction
9 files changed, 378 insertions, 264 deletions
diff --git a/src/jogl/classes/javax/media/opengl/GLContext.java b/src/jogl/classes/javax/media/opengl/GLContext.java index 38c2d79e5..2d304b77c 100644 --- a/src/jogl/classes/javax/media/opengl/GLContext.java +++ b/src/jogl/classes/javax/media/opengl/GLContext.java @@ -295,8 +295,11 @@ public abstract class GLContext { /** * Destroys this OpenGL context and frees its associated - * resources. The context should have been released before this - * method is called. + * resources. + * <p> + * The context may be current w/o recursion when calling <code>destroy()</code>, + * in which case this method destroys the context and releases the lock. + * </p> */ public abstract void destroy(); diff --git a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java index 1b234ef3a..cd341593e 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java @@ -59,6 +59,7 @@ import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; +import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.OffscreenLayerOption; import javax.media.nativewindow.WindowClosingProtocol; import javax.media.nativewindow.AbstractGraphicsDevice; @@ -441,25 +442,25 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing disposeRegenerate=regenerate; - if (Threading.isSingleThreaded() && - !Threading.isOpenGLThread()) { - // Workaround for termination issues with applets -- - // sun.applet.AppletPanel should probably be performing the - // remove() call on the EDT rather than on its own thread - // Hint: User should run remove from EDT. - if (ThreadingImpl.isAWTMode() && - Thread.holdsLock(getTreeLock())) { - // The user really should not be invoking remove() from this - // thread -- but since he/she is, we can not go over to the - // EDT at this point. Try to destroy the context from here. - if(context.isCreated()) { - drawableHelper.invokeGL(drawable, context, disposeAction, null); + if(context.isCreated()) { + if (Threading.isSingleThreaded() && + !Threading.isOpenGLThread()) { + // Workaround for termination issues with applets -- + // sun.applet.AppletPanel should probably be performing the + // remove() call on the EDT rather than on its own thread + // Hint: User should run remove from EDT. + if (ThreadingImpl.isAWTMode() && + Thread.holdsLock(getTreeLock())) { + // The user really should not be invoking remove() from this + // thread -- but since he/she is, we can not go over to the + // EDT at this point. Try to destroy the context from here. + drawableHelper.disposeGL(GLCanvas.this, drawable, context, postDisposeAction); + } else { + Threading.invokeOnOpenGLThread(disposeOnEventDispatchThreadAction); + } + } else { + drawableHelper.disposeGL(GLCanvas.this, drawable, context, postDisposeAction); } - } else if(context.isCreated()) { - Threading.invokeOnOpenGLThread(disposeOnEventDispatchThreadAction); - } - } else if(context.isCreated()) { - drawableHelper.invokeGL(drawable, context, disposeAction, null); } if(animatorPaused) { @@ -834,16 +835,9 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing } } - class DisposeAction implements Runnable { + class PostDisposeAction implements Runnable { public void run() { - drawableHelper.dispose(GLCanvas.this); - - if(null!=context) { - context.makeCurrent(); // implicit wait for lock .. - context.destroy(); - context=null; - } - + context=null; if(null!=drawable) { final JAWTWindow jawtWindow = (JAWTWindow)drawable.getNativeSurface(); drawable.setRealized(false); @@ -867,21 +861,22 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing } } private boolean disposeRegenerate; - private DisposeAction disposeAction = new DisposeAction(); + private PostDisposeAction postDisposeAction = new PostDisposeAction(); private DisposeOnEventDispatchThreadAction disposeOnEventDispatchThreadAction = new DisposeOnEventDispatchThreadAction(); class DisposeOnEventDispatchThreadAction implements Runnable { public void run() { - drawableHelper.invokeGL(drawable, context, disposeAction, null); + drawableHelper.disposeGL(GLCanvas.this, drawable, context, postDisposeAction); } } class DisposeAbstractGraphicsDeviceAction implements Runnable { public void run() { if(null != awtConfig) { - final AbstractGraphicsDevice adevice = awtConfig.getScreen().getDevice(); + final AbstractGraphicsConfiguration aconfig = awtConfig.getNativeGraphicsConfiguration(); + final AbstractGraphicsDevice adevice = aconfig.getScreen().getDevice(); final String adeviceMsg; if(DEBUG) { adeviceMsg = adevice.toString(); @@ -1091,9 +1086,9 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing } GLCapabilitiesImmutable caps = new GLCapabilities( GLProfile.getDefault(GLProfile.getDefaultDesktopDevice()) ); - Frame frame = new Frame("JOGL AWT Test"); + final Frame frame = new Frame("JOGL AWT Test"); - GLCanvas glCanvas = new GLCanvas(caps); + final GLCanvas glCanvas = new GLCanvas(caps); frame.add(glCanvas); frame.setSize(128, 128); @@ -1102,24 +1097,15 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing GL gl = drawable.getGL(); System.err.println(JoglVersion.getGLInfo(gl, null)); } - - public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { - } - - public void display(GLAutoDrawable drawable) { - } - - public void dispose(GLAutoDrawable drawable) { - } + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { } + public void display(GLAutoDrawable drawable) { } + public void dispose(GLAutoDrawable drawable) { } }); - final Frame _frame = frame; - final GLCanvas _glCanvas = glCanvas; - try { javax.swing.SwingUtilities.invokeAndWait(new Runnable() { public void run() { - _frame.setVisible(true); + frame.setVisible(true); }}); } catch (Throwable t) { t.printStackTrace(); @@ -1128,9 +1114,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing try { javax.swing.SwingUtilities.invokeAndWait(new Runnable() { public void run() { - _frame.setVisible(false); - _frame.remove(_glCanvas); - _frame.dispose(); + frame.dispose(); }}); } catch (Throwable t) { t.printStackTrace(); diff --git a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java index 0d28ab31b..7a87882ca 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java @@ -284,27 +284,24 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing animatorPaused = animator.pause(); } - final GLDrawable drawable = backend.getDrawable(); - final GLContext context = backend.getContext(); - - if (Threading.isSingleThreaded() && - !Threading.isOpenGLThread()) { - // Workaround for termination issues with applets -- - // sun.applet.AppletPanel should probably be performing the - // remove() call on the EDT rather than on its own thread - if (ThreadingImpl.isAWTMode() && - Thread.holdsLock(getTreeLock())) { - // The user really should not be invoking remove() from this - // thread -- but since he/she is, we can not go over to the - // EDT at this point. Try to destroy the context from here. - if(context.isCreated()) { - drawableHelper.invokeGL(drawable, context, disposeAction, null); - } - } else if(context.isCreated()) { - Threading.invokeOnOpenGLThread(disposeOnEventDispatchThreadAction); + if(backend.getContext().isCreated()) { + if (Threading.isSingleThreaded() && + !Threading.isOpenGLThread()) { + // Workaround for termination issues with applets -- + // sun.applet.AppletPanel should probably be performing the + // remove() call on the EDT rather than on its own thread + if (ThreadingImpl.isAWTMode() && + Thread.holdsLock(getTreeLock())) { + // The user really should not be invoking remove() from this + // thread -- but since he/she is, we can not go over to the + // EDT at this point. Try to destroy the context from here. + drawableHelper.disposeGL(GLJPanel.this, backend.getDrawable(), backend.getContext(), postDisposeAction); + } else { + Threading.invokeOnOpenGLThread(disposeOnEventDispatchThreadAction); + } + } else { + drawableHelper.disposeGL(GLJPanel.this, backend.getDrawable(), backend.getContext(), postDisposeAction); } - } else if(context.isCreated()) { - drawableHelper.invokeGL(drawable, context, disposeAction, null); } if(null != backend) { // not yet destroyed due to backend.isUsingOwnThreadManagment() == true @@ -689,12 +686,8 @@ public void reshape(int x, int y, int width, int height) { return "AWT-GLJPanel[ "+((null!=backend)?backend.getDrawable().getClass().getName():"null-drawable")+"]"; } - private DisposeAction disposeAction = new DisposeAction(); - - class DisposeAction implements Runnable { + class PostDisposeAction implements Runnable { public void run() { - updater.dispose(GLJPanel.this); - if (backend != null && !backend.isUsingOwnThreadManagment()) { backend.destroy(); backend = null; @@ -702,13 +695,14 @@ public void reshape(int x, int y, int width, int height) { } } } + private PostDisposeAction postDisposeAction = new PostDisposeAction(); private DisposeOnEventDispatchThreadAction disposeOnEventDispatchThreadAction = new DisposeOnEventDispatchThreadAction(); class DisposeOnEventDispatchThreadAction implements Runnable { public void run() { - drawableHelper.invokeGL(backend.getDrawable(), backend.getContext(), disposeAction, null); + drawableHelper.disposeGL(GLJPanel.this, backend.getDrawable(), backend.getContext(), postDisposeAction); } } @@ -1579,15 +1573,13 @@ public void reshape(int x, int y, int width, int height) { backend = null; oglPipelineEnabled = false; handleReshape = true; - j2dContext.release(); j2dContext.destroy(); j2dContext = null; return; } - j2dContext.release(); + } else { + j2dContext.makeCurrent(); } - - j2dContext.makeCurrent(); try { captureJ2DState(j2dContext.getGL(), g); Object curSurface = Java2D.getOGLSurfaceIdentifier(g); diff --git a/src/jogl/classes/jogamp/opengl/GLContextImpl.java b/src/jogl/classes/jogamp/opengl/GLContextImpl.java index 28bb11f1e..4701ab544 100644 --- a/src/jogl/classes/jogamp/opengl/GLContextImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLContextImpl.java @@ -69,6 +69,8 @@ import javax.media.opengl.GLPipelineFactory; import javax.media.opengl.GLProfile; public abstract class GLContextImpl extends GLContext { + public static final boolean TRACE_SWITCH = Debug.isPropertyDefined("jogl.debug.GLContext.TraceSwitch", true); + // RecursiveLock maintains a queue of waiting Threads, ensuring the longest waiting thread will be notified at unlock. protected RecursiveLock lock = LockFactory.createRecursiveLock(); @@ -213,74 +215,94 @@ public abstract class GLContextImpl extends GLContext { */ protected void drawableUpdatedNotify() throws GLException { } - boolean lockFailFast = true; - Object lockFailFastSync = new Object(); + volatile boolean lockFailFast = true; public boolean isSynchronized() { - synchronized (lockFailFastSync) { - return !lockFailFast; - } + return !lockFailFast; // volatile: ok } public void setSynchronized(boolean isSynchronized) { - synchronized (lockFailFastSync) { - lockFailFast = !isSynchronized; - } + lockFailFast = !isSynchronized; // volatile: ok } - private final void lockConsiderFailFast() { - synchronized (lockFailFastSync) { - if(lockFailFast && lock.isLockedByOtherThread()) { - throw new GLException("Error: Attempt to make context current on thread " + Thread.currentThread() + - " which is already current on thread " + lock.getOwner()); + private final void lockConsiderFailFast() { + if( lockFailFast ) { // volatile: ok + try { + if( !lock.tryLock(0) ) { // immediate return w/ false, if lock is already held by other thread + throw new GLException("Error: Attempt to make context current on thread " + Thread.currentThread().getName() + + " which is already current on thread " + lock.getOwner().getName()); + } + } catch (InterruptedException ie) { + throw new GLException(ie); } + } else { + lock.lock(); } - lock.lock(); } public abstract Object getPlatformGLExtensions(); // Note: the surface is locked within [makeCurrent .. swap .. release] public void release() throws GLException { + release(false); + } + private void release(boolean force) throws GLException { if ( !lock.isOwner() ) { throw new GLException("Context not current on current thread"); } - setCurrent(null); + final boolean actualRelease = force || lock.getHoldCount() == 1 ; try { - releaseImpl(); + if( actualRelease ) { + setCurrent(null); + if (contextHandle != 0) { // allow dbl-release + releaseImpl(); + } + } } finally { - if (drawable.isSurfaceLocked()) { - drawable.unlockSurface(); - } + drawable.unlockSurface(); lock.unlock(); + if(TRACE_SWITCH) { + if( actualRelease ) { + System.err.println("GLContext.ContextSwitch: - switch - CONTEXT_RELEASE - "+Thread.currentThread().getName()+" - "+lock); + } else { + System.err.println("GLContext.ContextSwitch: - keep - CONTEXT_RELEASE - "+Thread.currentThread().getName()+" - "+lock); + } + } } } protected abstract void releaseImpl() throws GLException; public final void destroy() { - if ( lock.isOwner() ) { - // release current context - if(null != glDebugHandler) { - glDebugHandler.enable(false); - } - release(); - } - // Must hold the lock around the destroy operation to make sure we // don't destroy the context out from under another thread rendering to it - lockConsiderFailFast(); + lockConsiderFailFast(); // holdCount++ -> 1 or 2 try { + if(lock.getHoldCount() > 2) { + throw new GLException("XXX: "+lock); + } + if (DEBUG || TRACE_SWITCH) { + System.err.println("GLContextImpl.destroy.0: " + toHexString(contextHandle) + + ", isShared "+GLContextShareSet.isShared(this)+" - "+lock); + } if (contextHandle != 0) { int lockRes = drawable.lockSurface(); if (NativeSurface.LOCK_SURFACE_NOT_READY == lockRes) { // this would be odd .. throw new GLException("Surface not ready to lock: "+drawable); } - try { - if (DEBUG) { - System.err.println("GLContextImpl.destroy: " + toHexString(contextHandle) + - ", isShared "+GLContextShareSet.isShared(this)); + // release current context + if(null != glDebugHandler) { + if(lock.getHoldCount() == 1) { + // needs current context to disable debug handler + makeCurrent(); } + glDebugHandler.enable(false); + } + if(lock.getHoldCount() > 1) { + // pending release() after makeCurrent() + release(true); + } + try { destroyImpl(); contextHandle = 0; glDebugHandler = null; @@ -294,6 +316,10 @@ public abstract class GLContextImpl extends GLContext { } } finally { lock.unlock(); + if (DEBUG || TRACE_SWITCH) { + System.err.println("GLContextImpl.destroy.X: " + toHexString(contextHandle) + + ", isShared "+GLContextShareSet.isShared(this)+" - "+lock); + } } resetStates(); @@ -362,55 +388,75 @@ public abstract class GLContextImpl extends GLContext { * @see #destroyContextARBImpl */ public int makeCurrent() throws GLException { - // One context can only be current by one thread, - // and one thread can only have one context current! - final GLContext current = getCurrent(); - if (current != null) { - if (current == this) { - // Assume we don't need to make this context current again - // For Mac OS X, however, we need to update the context to track resizes - drawableUpdatedNotify(); - return CONTEXT_CURRENT; - } else { - current.release(); - } - } - - if (GLWorkerThread.isStarted() && - !GLWorkerThread.isWorkerThread()) { - // Kick the GLWorkerThread off its current context - GLWorkerThread.invokeLater(new Runnable() { public void run() {} }); - } - - if (!isCreated()) { - // verify if the drawable has chosen Capabilities - if (null == getGLDrawable().getChosenGLCapabilities()) { - throw new GLException("drawable has no chosen GLCapabilities: "+getGLDrawable()); - } - if(DEBUG_GL) { - // only impacts w/ createContextARB(..) - additionalCtxCreationFlags |= GLContext.CTX_OPTION_DEBUG ; - } - } - + boolean unlockContextAndDrawable = false; lockConsiderFailFast(); - int res = 0; + int res = CONTEXT_NOT_CURRENT; try { - res = makeCurrentLocking(); - - /* FIXME: refactor dependence on Java 2D / JOGL bridge - if ((tracker != null) && - (res == CONTEXT_CURRENT_NEW)) { - // Increase reference count of GLObjectTracker - tracker.ref(); - } - */ - } catch (GLException e) { - lock.unlock(); - throw(e); + // Note: the surface is locked within [makeCurrent .. swap .. release] + int lockRes = drawable.lockSurface(); + if (NativeSurface.LOCK_SURFACE_NOT_READY >= lockRes) { + return CONTEXT_NOT_CURRENT; + } + try { + if (NativeSurface.LOCK_SURFACE_CHANGED == lockRes) { + drawable.updateHandle(); + } + // One context can only be current by one thread, + // and one thread can only have one context current! + final GLContext current = getCurrent(); + if (current != null) { + if (current == this) { + // Assume we don't need to make this context current again + // For Mac OS X, however, we need to update the context to track resizes + drawableUpdatedNotify(); + if(TRACE_SWITCH) { + System.err.println("GLContext.ContextSwitch: - keep - CONTEXT_CURRENT - "+Thread.currentThread().getName()+" - "+lock); + } + return CONTEXT_CURRENT; + } else { + current.release(); + } + } + if (GLWorkerThread.isStarted() && + !GLWorkerThread.isWorkerThread()) { + // Kick the GLWorkerThread off its current context + GLWorkerThread.invokeLater(new Runnable() { public void run() {} }); + } + + if (0 == drawable.getHandle()) { + throw new GLException("drawable has invalid handle: "+drawable); + } + res = makeCurrentWithinLock(lockRes); + unlockContextAndDrawable = CONTEXT_NOT_CURRENT == res; + + /** + * FIXME: refactor dependence on Java 2D / JOGL bridge + if ((tracker != null) && + (res == CONTEXT_CURRENT_NEW)) { + // Increase reference count of GLObjectTracker + tracker.ref(); + } + */ + } catch (RuntimeException e) { + unlockContextAndDrawable = true; + throw e; + } finally { + if (unlockContextAndDrawable) { + drawable.unlockSurface(); + } + } + } catch (RuntimeException e) { + unlockContextAndDrawable = true; + throw e; + } finally { + if (unlockContextAndDrawable) { + lock.unlock(); + } } if (res == CONTEXT_NOT_CURRENT) { - lock.unlock(); + if(TRACE_SWITCH) { + System.err.println("GLContext.ContextSwitch: - switch - CONTEXT_NOT_CURRENT - "+Thread.currentThread().getName()+" - "+lock); + } } else { setCurrent(this); if(res == CONTEXT_CURRENT_NEW) { @@ -429,6 +475,11 @@ public abstract class GLContextImpl extends GLContext { if(TRACE_GL) { gl = gl.getContext().setGL( GLPipelineFactory.create("javax.media.opengl.Trace", null, gl, new Object[] { System.err } ) ); } + if(TRACE_SWITCH) { + System.err.println("GLContext.ContextSwitch: - switch - CONTEXT_CURRENT_NEW - "+Thread.currentThread().getName()+" - "+lock); + } + } else if(TRACE_SWITCH) { + System.err.println("GLContext.ContextSwitch: - switch - CONTEXT_CURRENT - "+Thread.currentThread().getName()+" - "+lock); } /* FIXME: refactor dependence on Java 2D / JOGL bridge @@ -443,66 +494,47 @@ public abstract class GLContextImpl extends GLContext { return res; } - // Note: the surface is locked within [makeCurrent .. swap .. release] - protected final int makeCurrentLocking() throws GLException { - boolean shallUnlockSurface = false; - int lockRes = drawable.lockSurface(); - try { - if (NativeSurface.LOCK_SURFACE_NOT_READY >= lockRes) { - return CONTEXT_NOT_CURRENT; - } - try { - if (NativeSurface.LOCK_SURFACE_CHANGED == lockRes) { - drawable.updateHandle(); - } - if (0 == drawable.getHandle()) { - throw new GLException("drawable has invalid handle: "+drawable); - } - if (!isCreated()) { - final GLContextImpl shareWith = (GLContextImpl) GLContextShareSet.getShareContext(this); + private final int makeCurrentWithinLock(int surfaceLockRes) throws GLException { + if (!isCreated()) { + if(DEBUG_GL) { + // only impacts w/ createContextARB(..) + additionalCtxCreationFlags |= GLContext.CTX_OPTION_DEBUG ; + } + + final GLContextImpl shareWith = (GLContextImpl) GLContextShareSet.getShareContext(this); + if (null != shareWith) { + shareWith.getDrawableImpl().lockSurface(); + } + final boolean created; + try { + created = createImpl(shareWith); // may throws exception if fails! + } finally { if (null != shareWith) { - shareWith.getDrawableImpl().lockSurface(); - } - boolean created; - try { - created = createImpl(shareWith); // may throws exception if fails! - } finally { - if (null != shareWith) { - shareWith.getDrawableImpl().unlockSurface(); - } - } - if (DEBUG) { - if(created) { - System.err.println(getThreadName() + ": !!! Create GL context OK: " + toHexString(contextHandle) + " for " + getClass().getName()); - } else { - System.err.println(getThreadName() + ": !!! Create GL context FAILED for " + getClass().getName()); - } - } - if(!created) { - shallUnlockSurface = true; - return CONTEXT_NOT_CURRENT; + shareWith.getDrawableImpl().unlockSurface(); + } + } + if (DEBUG) { + if(created) { + System.err.println(getThreadName() + ": !!! Create GL context OK: " + toHexString(contextHandle) + " for " + getClass().getName()); + } else { + System.err.println(getThreadName() + ": !!! Create GL context FAILED for " + getClass().getName()); } - GLContextShareSet.contextCreated(this); - return CONTEXT_CURRENT_NEW; - } - makeCurrentImpl(); - return CONTEXT_CURRENT; - } catch (RuntimeException e) { - shallUnlockSurface = true; - throw e; - } - } finally { - if (shallUnlockSurface) { - drawable.unlockSurface(); + } + if(!created) { + return CONTEXT_NOT_CURRENT; + } + GLContextShareSet.contextCreated(this); + return CONTEXT_CURRENT_NEW; } - } + makeCurrentImpl(); + return CONTEXT_CURRENT; } protected abstract void makeCurrentImpl() throws GLException; /** * Platform dependent entry point for context creation.<br> * - * This method is called from {@link #makeCurrentLocking()} .. {@link #makeCurrent()} .<br> + * This method is called from {@link #makeCurrentWithinLock()} .. {@link #makeCurrent()} .<br> * * The implementation shall verify this context with a * <code>MakeContextCurrent</code> call.<br> diff --git a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java index 41b4ea878..509839f55 100644 --- a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java +++ b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java @@ -57,6 +57,9 @@ import com.jogamp.opengl.util.Animator; methods to be able to share it between GLCanvas and GLJPanel. */ public class GLDrawableHelper { + /** true if property <code>jogl.debug.GLDrawable.PerfStats</code> is defined. */ + private static final boolean PERF_STATS = Debug.isPropertyDefined("jogl.debug.GLDrawable.PerfStats", true); + protected static final boolean DEBUG = GLDrawableImpl.DEBUG; private Object listenersLock = new Object(); private ArrayList<GLEventListener> listeners; @@ -128,6 +131,10 @@ public class GLDrawableHelper { /** * Issues {@link javax.media.opengl.GLEventListener#dispose(javax.media.opengl.GLAutoDrawable)} * to all listeners. + * <p> + * Please consider using {@link #disposeGL(GLAutoDrawable, GLDrawable, GLContext, Runnable)} + * for correctness! + * </p> * @param drawable */ public final void dispose(GLAutoDrawable drawable) { @@ -137,7 +144,7 @@ public class GLDrawableHelper { } } } - + private boolean init(GLEventListener l, GLAutoDrawable drawable, boolean sendReshape) { if(listenersToBeInit.remove(l)) { l.init(drawable); @@ -307,6 +314,8 @@ public class GLDrawableHelper { * ie. {@link Animator#getThread()}. * @deprecated this is an experimental feature, * intended for measuring performance in regards to GL context switch + * and only being used if {@link #PERF_STATS} is enabled + * by defining property <code>jogl.debug.GLDrawable.PerfStats</code>. */ public final void setSkipContextReleaseThread(Thread t) { skipContextReleaseThread = t; @@ -318,7 +327,7 @@ public class GLDrawableHelper { public final Thread getSkipContextReleaseThread() { return skipContextReleaseThread; } - + private static final ThreadLocal<Runnable> perThreadInitAction = new ThreadLocal<Runnable>(); /** Principal helper method which runs a Runnable with the context @@ -328,8 +337,6 @@ public class GLDrawableHelper { 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 @@ -348,12 +355,112 @@ public class GLDrawableHelper { return; } - final boolean isDisposeAction = null==initAction ; - - if( isDisposeAction && !context.isCreated() ) { - throw new GLException(Thread.currentThread().getName()+" GLDrawableHelper " + this + ".invokeGL(): Dispose case (no init action given): Native context is not created: "+context); + if(PERF_STATS) { + invokeGLImplStats(drawable, context, runnable, initAction, null); + } else { + invokeGLImpl(drawable, context, runnable, initAction, null); } + } + /** + * Principal helper method which runs {@link #dispose(GLAutoDrawable)} with the context + * made current and destroys the context afterwards while holding the lock. + * + * @param autoDrawable + * @param drawable + * @param context + * @param postAction + */ + public final void disposeGL(GLAutoDrawable autoDrawable, + GLDrawable drawable, + GLContext context, + Runnable postAction) { + if(PERF_STATS) { + invokeGLImplStats(drawable, context, null, null, autoDrawable); + } else { + invokeGLImpl(drawable, context, null, null, autoDrawable); + } + if(null != postAction) { + postAction.run(); + } + } + + private final void invokeGLImpl(GLDrawable drawable, + GLContext context, + Runnable runnable, + Runnable initAction, + GLAutoDrawable disposeAutoDrawable) { + final Thread currentThread = Thread.currentThread(); + + final boolean isDisposeAction = null==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 = null; + if (lastContext != null) { + if (lastContext == context) { + lastContext = null; // utilize recursive locking + } else { + lastInitAction = perThreadInitAction.get(); + lastContext.release(); + } + } + int res = GLContext.CONTEXT_NOT_CURRENT; + + try { + res = context.makeCurrent(); + if (res != GLContext.CONTEXT_NOT_CURRENT) { + if(!isDisposeAction) { + perThreadInitAction.set(initAction); + if (res == GLContext.CONTEXT_CURRENT_NEW) { + if (DEBUG) { + System.err.println("GLDrawableHelper " + this + ".invokeGL(): Running initAction"); + } + initAction.run(); + } + runnable.run(); + if (autoSwapBufferMode) { + drawable.swapBuffers(); + } + } else { + if(res == GLContext.CONTEXT_CURRENT_NEW) { + throw new GLException(currentThread.getName()+" GLDrawableHelper " + this + ".invokeGL(): Dispose case (no init action given): Native context was not created (new ctx): "+context); + } + if(listeners.size()>0) { + dispose(disposeAutoDrawable); + } + } + } + } finally { + try { + if(isDisposeAction) { + context.destroy(); + } else if( res != GLContext.CONTEXT_NOT_CURRENT ) { + context.release(); + } + } catch (Exception e) { + System.err.println("Catched: "+e.getMessage()); + e.printStackTrace(); + } + if (lastContext != null) { + final int res2 = lastContext.makeCurrent(); + if (null != lastInitAction && res2 == GLContext.CONTEXT_CURRENT_NEW) { + lastInitAction.run(); + } + } + } + } + + private final void invokeGLImplStats(GLDrawable drawable, + GLContext context, + Runnable runnable, + Runnable initAction, + GLAutoDrawable disposeAutoDrawable) { + final Thread currentThread = Thread.currentThread(); + + final boolean isDisposeAction = null==initAction ; + // Support for recursive makeCurrent() calls as well as calling // other drawables' display() methods from within another one's int res = GLContext.CONTEXT_NOT_CURRENT; @@ -361,7 +468,9 @@ public class GLDrawableHelper { Runnable lastInitAction = null; if (lastContext != null) { if (lastContext == context) { - res = GLContext.CONTEXT_CURRENT; + if( currentThread == skipContextReleaseThread ) { + res = GLContext.CONTEXT_CURRENT; + } // else: utilize recursive locking lastContext = null; } else { lastInitAction = perThreadInitAction.get(); @@ -369,17 +478,18 @@ public class GLDrawableHelper { } } - /** long t0 = System.currentTimeMillis(); - long td1 = 0; // makeCurrent + long tdA = 0; // makeCurrent long tdR = 0; // render time - long td2 = 0; // swapBuffers - long td3 = 0; // release - boolean scr = true; */ - + long tdS = 0; // swapBuffers + long tdX = 0; // release + boolean ctxClaimed = false; + boolean ctxReleased = false; + boolean ctxDestroyed = false; try { if (res == GLContext.CONTEXT_NOT_CURRENT) { res = context.makeCurrent(); + ctxClaimed = true; } if (res != GLContext.CONTEXT_NOT_CURRENT) { if(!isDisposeAction) { @@ -390,39 +500,50 @@ public class GLDrawableHelper { } initAction.run(); } - } - // tdR = System.currentTimeMillis(); - // td1 = tdR - t0; // makeCurrent - if(null!=runnable) { + tdR = System.currentTimeMillis(); + tdA = tdR - t0; // makeCurrent runnable.run(); - // td2 = System.currentTimeMillis(); - // tdR = td2 - tdR; // render time - if (autoSwapBufferMode && !isDisposeAction && drawable != null) { + tdS = System.currentTimeMillis(); + tdR = tdS - tdR; // render time + if (autoSwapBufferMode) { drawable.swapBuffers(); - // td3 = System.currentTimeMillis(); - // td2 = td3 - td2; // swapBuffers + tdX = System.currentTimeMillis(); + tdS = tdX - tdS; // swapBuffers + } + } else { + if(res == GLContext.CONTEXT_CURRENT_NEW) { + throw new GLException(currentThread.getName()+" GLDrawableHelper " + this + ".invokeGL(): Dispose case (no init action given): Native context was not created (new ctx): "+context); + } + if(listeners.size()>0) { + dispose(disposeAutoDrawable); } } } } finally { - if(res != GLContext.CONTEXT_NOT_CURRENT && - (null == skipContextReleaseThread || Thread.currentThread()!=skipContextReleaseThread) ) { - try { + try { + if(isDisposeAction) { + context.destroy(); + ctxDestroyed = true; + } else if( res != GLContext.CONTEXT_NOT_CURRENT && + (null == skipContextReleaseThread || currentThread != skipContextReleaseThread) ) { context.release(); - // scr = false; - } catch (Exception e) { + ctxReleased = true; } + } catch (Exception e) { + System.err.println("Catched: "+e.getMessage()); + e.printStackTrace(); } - // td3 = System.currentTimeMillis() - td3; // release + + tdX = System.currentTimeMillis() - tdX; // release / destroy if (lastContext != null) { - int res2 = lastContext.makeCurrent(); + final int res2 = lastContext.makeCurrent(); if (null != lastInitAction && res2 == GLContext.CONTEXT_CURRENT_NEW) { lastInitAction.run(); } } } - // long td0 = System.currentTimeMillis() - t0; - // System.err.println("td0 "+td0+"ms, fps "+(1.0/(td0/1000.0))+", td-makeCurrent: "+td1+"ms, td-render "+tdR+"ms, td-swap "+td2+"ms, td-release "+td3+"ms, skip ctx release: "+scr); + long td = System.currentTimeMillis() - t0; + System.err.println("td0 "+td+"ms, fps "+(1.0/(td/1000.0))+", td-makeCurrent: "+tdA+"ms, td-render "+tdR+"ms, td-swap "+tdS+"ms, td-release "+tdX+"ms, ctx claimed: "+ctxClaimed+", ctx release: "+ctxReleased+", ctx destroyed "+ctxDestroyed); } - + } diff --git a/src/jogl/classes/jogamp/opengl/GLPbufferImpl.java b/src/jogl/classes/jogamp/opengl/GLPbufferImpl.java index ae83965e9..10d5a3f27 100644 --- a/src/jogl/classes/jogamp/opengl/GLPbufferImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLPbufferImpl.java @@ -99,28 +99,21 @@ public class GLPbufferImpl implements GLPbuffer { return true; } - class DisposeAction implements Runnable { - public void run() { - // Lock: Covered by DestroyAction .. - drawableHelper.dispose(GLPbufferImpl.this); - } - } - DisposeAction disposeAction = new DisposeAction(); - public void destroy() { if(pbufferDrawable.isRealized()) { final AbstractGraphicsDevice adevice = pbufferDrawable.getNativeSurface().getGraphicsConfiguration().getScreen().getDevice(); if (null != context && context.isCreated()) { try { - drawableHelper.invokeGL(pbufferDrawable, context, disposeAction, null); + drawableHelper.disposeGL(GLPbufferImpl.this, pbufferDrawable, context, null); } catch (GLException gle) { gle.printStackTrace(); } - context.destroy(); + context = null; // drawableHelper.reset(); } pbufferDrawable.destroy(); + pbufferDrawable = null; if(null != adevice) { adevice.close(); diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawableFactory.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawableFactory.java index 1e55879f7..3dd7a7f08 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawableFactory.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawableFactory.java @@ -234,7 +234,6 @@ public class MacOSXCGLDrawableFactory extends GLDrawableFactoryImpl { gle.printStackTrace(); } } finally { - context.release(); context.destroy(); } } diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawableFactory.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawableFactory.java index 235aea499..acced638f 100644 --- a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawableFactory.java +++ b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawableFactory.java @@ -283,7 +283,6 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl { if (null != sr.context) { // may cause JVM SIGSEGV: - sr.context.makeCurrent(); sr.context.destroy(); sr.context = null; } diff --git a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java index d03a475ee..1a15a9664 100644 --- a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java +++ b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java @@ -355,14 +355,6 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC // Hide methods here .. protected class GLLifecycleHook implements WindowImpl.LifecycleHook { - private class DisposeAction implements Runnable { - public final void run() { - // Lock: Covered by DestroyAction .. - helper.dispose(GLWindow.this); - } - } - DisposeAction disposeAction = new DisposeAction(); - public synchronized void destroyActionPreLock() { // nop } @@ -380,11 +372,10 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC // Catch dispose GLExceptions by GLEventListener, just 'print' them // so we can continue with the destruction. try { - helper.invokeGL(drawable, context, disposeAction, null); + helper.disposeGL(GLWindow.this, drawable, context, null); } catch (GLException gle) { gle.printStackTrace(); } - context.destroy(); } drawable.setRealized(false); } |