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 /src/jogl/classes/jogamp/opengl/GLContextImpl.java | |
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
Diffstat (limited to 'src/jogl/classes/jogamp/opengl/GLContextImpl.java')
-rw-r--r-- | src/jogl/classes/jogamp/opengl/GLContextImpl.java | 288 |
1 files changed, 160 insertions, 128 deletions
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> |