diff options
Diffstat (limited to 'src/jogl/classes/jogamp/opengl/GLContextImpl.java')
-rw-r--r-- | src/jogl/classes/jogamp/opengl/GLContextImpl.java | 1872 |
1 files changed, 1437 insertions, 435 deletions
diff --git a/src/jogl/classes/jogamp/opengl/GLContextImpl.java b/src/jogl/classes/jogamp/opengl/GLContextImpl.java index 51201b3a9..9ccd78589 100644 --- a/src/jogl/classes/jogamp/opengl/GLContextImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLContextImpl.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 JogAmp Community. 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 @@ -29,39 +29,51 @@ * 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 jogamp.opengl; +import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.IntBuffer; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.HashMap; import java.util.Map; import com.jogamp.common.os.DynamicLookupHelper; +import com.jogamp.common.os.Platform; import com.jogamp.common.util.ReflectionUtil; -import com.jogamp.gluegen.runtime.FunctionAddressResolver; +import com.jogamp.common.util.VersionNumber; +import com.jogamp.common.util.VersionNumberString; +import com.jogamp.common.util.locks.RecursiveLock; import com.jogamp.gluegen.runtime.ProcAddressTable; -import com.jogamp.gluegen.runtime.opengl.GLExtensionNames; +import com.jogamp.gluegen.runtime.opengl.GLNameResolver; import com.jogamp.gluegen.runtime.opengl.GLProcAddressResolver; +import com.jogamp.opengl.GLExtensions; +import com.jogamp.opengl.GLRendererQuirks; import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.NativeWindowFactory; import javax.media.opengl.GL; -import javax.media.opengl.GL3; +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GL2ES3; +import javax.media.opengl.GL2GL3; import javax.media.opengl.GLCapabilitiesImmutable; import javax.media.opengl.GLContext; import javax.media.opengl.GLDebugListener; import javax.media.opengl.GLDebugMessage; import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLException; import javax.media.opengl.GLPipelineFactory; import javax.media.opengl.GLProfile; @@ -74,7 +86,7 @@ public abstract class GLContextImpl extends GLContext { private String contextFQN; private int additionalCtxCreationFlags; - + // Cache of the functions that are available to be called at the current // moment in time protected ExtensionAvailabilityCache extensionAvailability; @@ -82,19 +94,26 @@ public abstract class GLContextImpl extends GLContext { // OpenGL functions. private ProcAddressTable glProcAddressTable; + private String glVendor; private String glRenderer; private String glRendererLowerCase; - - // Tracks creation and initialization of buffer objects to avoid + private String glVersion; + + // Tracks lifecycle of buffer objects to avoid // repeated glGet calls upon glMapBuffer operations - private GLBufferSizeTracker bufferSizeTracker; // Singleton - Set by GLContextShareSet - private GLBufferStateTracker bufferStateTracker = new GLBufferStateTracker(); - private GLStateTracker glStateTracker = new GLStateTracker(); + private final GLBufferObjectTracker bufferObjectTracker; + private final GLBufferStateTracker bufferStateTracker; + private final GLStateTracker glStateTracker = new GLStateTracker(); private GLDebugMessageHandler glDebugHandler = null; - + private final int[] boundFBOTarget = new int[] { 0, 0 }; // { draw, read } + private int defaultVAO = 0; + protected GLDrawableImpl drawable; protected GLDrawableImpl drawableRead; + private volatile boolean pixelDataEvaluated; + private int /* pixelDataInternalFormat, */ pixelDataFormat, pixelDataType; + protected GL gl; protected static final Object mappedContextTypeObjectLock; @@ -112,72 +131,128 @@ public abstract class GLContextImpl extends GLContext { public static void shutdownImpl() { mappedExtensionAvailabilityCache.clear(); mappedGLProcAddress.clear(); - mappedGLXProcAddress.clear(); + mappedGLXProcAddress.clear(); } - + public GLContextImpl(GLDrawableImpl drawable, GLContext shareWith) { super(); - if (shareWith != null) { + bufferStateTracker = new GLBufferStateTracker(); + if ( null != shareWith ) { GLContextShareSet.registerSharing(this, shareWith); + bufferObjectTracker = ((GLContextImpl)shareWith).getBufferObjectTracker(); + assert (bufferObjectTracker != null) : "shared context hash null GLBufferObjectTracker: "+shareWith; + } else { + bufferObjectTracker = new GLBufferObjectTracker(); } - GLContextShareSet.synchronizeBufferObjectSharing(shareWith, this); this.drawable = drawable; this.drawableRead = drawable; - + this.glDebugHandler = new GLDebugMessageHandler(this); } - @Override - protected void resetStates() { - // Because we don't know how many other contexts we might be - // sharing with (and it seems too complicated to implement the - // GLObjectTracker's ref/unref scheme for the buffer-related - // optimizations), simply clear the cache of known buffers' sizes - // when we destroy contexts - if (bufferSizeTracker != null) { - bufferSizeTracker.clearCachedBufferSizes(); - } - - if (bufferStateTracker != null) { - bufferStateTracker.clearBufferObjectState(); + private final void clearStates() { + if( !GLContextShareSet.hasCreatedSharedLeft(this) ) { + bufferObjectTracker.clear(); } + bufferStateTracker.clear(); + glStateTracker.setEnabled(false); + glStateTracker.clearStates(); + } - if (glStateTracker != null) { - glStateTracker.clearStates(false); + @Override + protected void resetStates(boolean isInit) { + if( !isInit ) { + clearStates(); } - extensionAvailability = null; glProcAddressTable = null; gl = null; contextFQN = null; additionalCtxCreationFlags = 0; - glRenderer = ""; + glVendor = ""; + glRenderer = glVendor; glRendererLowerCase = glRenderer; - - super.resetStates(); + glVersion = glVendor; + + if (boundFBOTarget != null) { // <init> + boundFBOTarget[0] = 0; // draw + boundFBOTarget[1] = 0; // read + } + + pixelDataEvaluated = false; + + super.resetStates(isInit); } - public final void setGLReadDrawable(GLDrawable read) { - if(null!=read && drawable!=read && !isGLReadDrawableAvailable()) { - throw new GLException("GL Read Drawable not available"); + @Override + public final GLDrawable setGLReadDrawable(GLDrawable read) { + if(!isGLReadDrawableAvailable()) { + throw new GLException("Setting read drawable feature not available"); } - boolean lockHeld = lock.isOwner(); + final boolean lockHeld = lock.isOwner(Thread.currentThread()); if(lockHeld) { release(); + } else if(lock.isLockedByOtherThread()) { // still could glitch .. + throw new GLException("GLContext current by other thread ("+lock.getOwner()+"), operation not allowed."); } + final GLDrawable old = drawableRead; drawableRead = ( null != read ) ? (GLDrawableImpl) read : drawable; if(lockHeld) { makeCurrent(); } + return old; } + @Override public final GLDrawable getGLReadDrawable() { return drawableRead; } + @Override + public final GLDrawable setGLDrawable(GLDrawable readWrite, boolean setWriteOnly) { + if( drawable == readWrite && ( setWriteOnly || drawableRead == readWrite ) ) { + return drawable; // no change. + } + final Thread currentThread = Thread.currentThread(); + if( lock.isLockedByOtherThread() ) { + throw new GLException("GLContext current by other thread "+lock.getOwner().getName()+", operation not allowed on this thread "+currentThread.getName()); + } + final boolean lockHeld = lock.isOwner(currentThread); + if( lockHeld && lock.getHoldCount() > 1 ) { + // would need to makeCurrent * holdCount + throw new GLException("GLContext is recursively locked - unsupported for setGLDrawable(..)"); + } + final GLDrawableImpl old = drawable; + if( isCreated() && null != old && old.isRealized() ) { + if(!lockHeld) { + makeCurrent(); + } + associateDrawable(false); + if(!lockHeld) { + release(); + } + } + if(lockHeld) { + release(); + } + if( !setWriteOnly || drawableRead == drawable ) { // if !setWriteOnly || !explicitReadDrawable + drawableRead = (GLDrawableImpl) readWrite; + } + drawableRetargeted |= null != drawable && readWrite != drawable; + drawable = (GLDrawableImpl) readWrite ; + if( isCreated() && null != drawable && drawable.isRealized() ) { + makeCurrent(true); // implicit: associateDrawable(true) + if( !lockHeld ) { + release(); + } + } + return old; + } + + @Override public final GLDrawable getGLDrawable() { return drawable; } @@ -186,30 +261,48 @@ public abstract class GLContextImpl extends GLContext { return (GLDrawableImpl) getGLDrawable(); } + @Override + public final GL getRootGL() { + GL _gl = gl; + GL _parent = _gl.getDownstreamGL(); + while ( null != _parent ) { + _gl = _parent; + _parent = _gl.getDownstreamGL(); + } + return _gl; + } + + @Override public final GL getGL() { return gl; } + @Override public GL setGL(GL gl) { - if(DEBUG) { - String sgl1 = (null!=this.gl)?this.gl.getClass().getSimpleName()+", "+this.gl.toString():"<null>"; - String sgl2 = (null!=gl)?gl.getClass().getSimpleName()+", "+gl.toString():"<null>"; - Exception e = new Exception("Info: setGL (OpenGL "+getGLVersion()+"): "+Thread.currentThread().getName()+", "+sgl1+" -> "+sgl2); - e.printStackTrace(); + if( DEBUG ) { + final String sgl1 = (null!=this.gl)?this.gl.getClass().getSimpleName()+", "+this.gl.toString():"<null>"; + final String sgl2 = (null!=gl)?gl.getClass().getSimpleName()+", "+gl.toString():"<null>"; + System.err.println("Info: setGL (OpenGL "+getGLVersion()+"): "+getThreadName()+", "+sgl1+" -> "+sgl2); + Thread.dumpStack(); } this.gl = gl; return gl; } + @Override + public final int getDefaultVAO() { + return defaultVAO; + } + /** * Call this method to notify the OpenGL context * that the drawable has changed (size or position). - * + * * <p> - * This is currently being used and overridden by Mac OSX, + * This is currently being used and overridden by Mac OSX, * which issues the {@link jogamp.opengl.macosx.cgl.CGL#updateContext(long) NSOpenGLContext update()} call. * </p> - * + * * @throws GLException */ protected void drawableUpdatedNotify() throws GLException { } @@ -217,22 +310,40 @@ public abstract class GLContextImpl extends GLContext { public abstract Object getPlatformGLExtensions(); // Note: the surface is locked within [makeCurrent .. swap .. release] + @Override public void release() throws GLException { release(false); } - private void release(boolean force) throws GLException { - if(TRACE_SWITCH) { - System.err.println(getThreadName() +": GLContext.ContextSwitch: - release() - force: "+force+", "+lock); + private void release(boolean inDestruction) throws GLException { + if( TRACE_SWITCH ) { + System.err.println(getThreadName() +": GLContext.ContextSwitch[release.0]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+", inDestruction: "+inDestruction+", "+lock); } - if ( !lock.isOwner() ) { - throw new GLException("Context not current on current thread "+Thread.currentThread().getName()+": "+this); + if ( !lock.isOwner(Thread.currentThread()) ) { + final String msg = getThreadName() +": Context not current on thread, obj " + toHexString(hashCode())+", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+", inDestruction: "+inDestruction+", "+lock; + if( DEBUG_TRACE_SWITCH ) { + System.err.println(msg); + if( null != lastCtxReleaseStack ) { + System.err.print("Last release call: "); + lastCtxReleaseStack.printStackTrace(); + } else { + System.err.println("Last release call: NONE"); + } + } + throw new GLException(msg); } - final boolean actualRelease = force || lock.getHoldCount() == 1 ; - try { + + Throwable drawableContextMadeCurrentException = null; + final boolean actualRelease = ( inDestruction || lock.getHoldCount() == 1 ) && 0 != contextHandle; + try { if( actualRelease ) { - if (contextHandle != 0) { // allow dbl-release - releaseImpl(); + if( !inDestruction ) { + try { + contextMadeCurrent(false); + } catch (Throwable t) { + drawableContextMadeCurrentException = t; + } } + releaseImpl(); } } finally { // exception prone .. @@ -241,68 +352,106 @@ public abstract class GLContextImpl extends GLContext { } drawable.unlockSurface(); lock.unlock(); - if(TRACE_SWITCH) { - System.err.println(getThreadName() +": GLContext.ContextSwitch: - "+(actualRelease?"switch":"keep ")+" - CONTEXT_RELEASE - "+lock); + if( DEBUG_TRACE_SWITCH ) { + final String msg = getThreadName() +": GLContext.ContextSwitch[release.X]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+" - "+(actualRelease?"switch":"keep ")+" - "+lock; + lastCtxReleaseStack = new Throwable(msg); + if( TRACE_SWITCH ) { + System.err.println(msg); + // Thread.dumpStack(); + } } } + if(null != drawableContextMadeCurrentException) { + throw new GLException("GLContext.release(false) during GLDrawableImpl.contextMadeCurrent(this, false)", drawableContextMadeCurrentException); + } + } + private Throwable lastCtxReleaseStack = null; protected abstract void releaseImpl() throws GLException; + @Override public final void destroy() { - if (DEBUG || TRACE_SWITCH) { - System.err.println(getThreadName() + ": GLContextImpl.destroy.0: " + toHexString(contextHandle) + - ", isShared "+GLContextShareSet.isShared(this)+" - "+lock); + if ( DEBUG_TRACE_SWITCH ) { + final long drawH = null != drawable ? drawable.getHandle() : 0; + System.err.println(getThreadName() + ": GLContextImpl.destroy.0: obj " + toHexString(hashCode()) + ", ctx " + toHexString(contextHandle) + + ", surf "+toHexString(drawH)+", isShared "+GLContextShareSet.isShared(this)+" - "+lock); } - if (contextHandle != 0) { - int lockRes = drawable.lockSurface(); - if (NativeSurface.LOCK_SURFACE_NOT_READY == lockRes) { + if ( 0 != contextHandle ) { // isCreated() ? + if ( null == drawable ) { + throw new GLException("GLContext created but drawable is null: "+toString()); + } + final int lockRes = drawable.lockSurface(); + if ( NativeSurface.LOCK_SURFACE_NOT_READY >= lockRes ) { // this would be odd .. throw new GLException("Surface not ready to lock: "+drawable); } + Throwable associateDrawableException = null; try { + if ( !drawable.isRealized() ) { + throw new GLException("GLContext created but drawable not realized: "+toString()); + } // Must hold the lock around the destroy operation to make sure we // don't destroy the context while another thread renders to it. - // FIXME: This is actually impossible now, since we acquired the surface lock already, - // which is a prerequisite to acquire the context lock. - lock.lock(); // holdCount++ -> 1 or 2 + lock.lock(); // holdCount++ -> 1 - n (1: not locked, 2-n: destroy while rendering) if ( lock.getHoldCount() > 2 ) { - throw new GLException(getThreadName() + ": Lock was hold more than once - makeCurrent/release imbalance: "+lock); + final String msg = getThreadName() + ": GLContextImpl.destroy: obj " + toHexString(hashCode()) + ", ctx " + toHexString(contextHandle); + if ( DEBUG_TRACE_SWITCH ) { + System.err.println(msg+" - Lock was hold more than once - makeCurrent/release imbalance: "+lock); + Thread.dumpStack(); + } } - try { - // release current context - if(null != glDebugHandler) { - if(lock.getHoldCount() == 1) { - // needs current context to disable debug handler - makeCurrent(); + try { + // if not current, makeCurrent(), to call associateDrawable(..) and to disable debug handler + if ( lock.getHoldCount() == 1 ) { + if ( GLContext.CONTEXT_NOT_CURRENT == makeCurrent() ) { + throw new GLException("GLContext.makeCurrent() failed: "+toString()); } - glDebugHandler.enable(false); } + try { + associateDrawable(false); + } catch (Throwable t) { + associateDrawableException = t; + } + if ( 0 != defaultVAO ) { + final int[] tmp = new int[] { defaultVAO }; + final GL2ES3 gl2es3 = gl.getRootGL().getGL2ES3(); + gl2es3.glBindVertexArray(0); + gl2es3.glDeleteVertexArrays(1, tmp, 0); + defaultVAO = 0; + } + glDebugHandler.enable(false); if(lock.getHoldCount() > 1) { // pending release() after makeCurrent() - release(true); + release(true); } destroyImpl(); contextHandle = 0; glDebugHandler = null; // this maybe impl. in a platform specific way to release remaining shared ctx. - if(GLContextShareSet.contextDestroyed(this) && !GLContextShareSet.hasCreatedSharedLeft(this)) { + if( GLContextShareSet.contextDestroyed(this) && !GLContextShareSet.hasCreatedSharedLeft(this) ) { GLContextShareSet.unregisterSharing(this); } + resetStates(false); } finally { lock.unlock(); - if (TRACE_SWITCH) { - System.err.println(getThreadName() + ": GLContextImpl.destroy.X: " + toHexString(contextHandle) + + if ( DEBUG_TRACE_SWITCH ) { + System.err.println(getThreadName() + ": GLContextImpl.destroy.X: obj " + toHexString(hashCode()) + ", ctx " + toHexString(contextHandle) + ", isShared "+GLContextShareSet.isShared(this)+" - "+lock); } } } finally { drawable.unlockSurface(); } + if( null != associateDrawableException ) { + throw new GLException("Exception @ destroy's associateDrawable(false)", associateDrawableException); + } + } else { + resetStates(false); } - resetStates(); } protected abstract void destroyImpl() throws GLException; + @Override public final void copy(GLContext source, int mask) throws GLException { if (source.getHandle() == 0) { throw new GLException("Source OpenGL context has not been created"); @@ -311,8 +460,8 @@ public abstract class GLContextImpl extends GLContext { throw new GLException("Destination OpenGL context has not been created"); } - int lockRes = drawable.lockSurface(); - if (NativeSurface.LOCK_SURFACE_NOT_READY == lockRes) { + final int lockRes = drawable.lockSurface(); + if (NativeSurface.LOCK_SURFACE_NOT_READY >= lockRes) { // this would be odd .. throw new GLException("Surface not ready to lock"); } @@ -364,98 +513,110 @@ public abstract class GLContextImpl extends GLContext { * @see #mapVersionAvailable * @see #destroyContextARBImpl */ - public int makeCurrent() throws GLException { - boolean unlockContextAndDrawable = false; - int res = CONTEXT_NOT_CURRENT; + @Override + public final int makeCurrent() throws GLException { + return makeCurrent(false); + } + + protected final int makeCurrent(boolean forceDrawableAssociation) throws GLException { + if( TRACE_SWITCH ) { + System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.0]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+" - "+lock); + } // Note: the surface is locked within [makeCurrent .. swap .. release] - int lockRes = drawable.lockSurface(); + final int lockRes = drawable.lockSurface(); if (NativeSurface.LOCK_SURFACE_NOT_READY >= lockRes) { + if( DEBUG_TRACE_SWITCH ) { + System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.X1]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+" - Surface Not Ready - CONTEXT_NOT_CURRENT - "+lock); + } return CONTEXT_NOT_CURRENT; } + + boolean unlockResources = true; // Must be cleared if successful, otherwise finally block will release context and/or surface! + int res = CONTEXT_NOT_CURRENT; try { - if (NativeSurface.LOCK_SURFACE_CHANGED == lockRes) { - drawable.updateHandle(); - } - - lock.lock(); - try { - // 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(getThreadName() +": GLContext.ContextSwitch: - keep - CONTEXT_CURRENT - "+lock); - } - return CONTEXT_CURRENT; - } else { - current.release(); - } - } - if (0 == drawable.getHandle()) { + if ( drawable.isRealized() ) { + 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(); + lock.lock(); + try { + // 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) { // implicit recursive locking! + // 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(); + unlockResources = false; // success + if( TRACE_SWITCH ) { + System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.X2]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+" - keep - CONTEXT_CURRENT - "+lock); + } + return CONTEXT_CURRENT; + } else { + current.release(); + } } - */ - } catch (RuntimeException e) { - unlockContextAndDrawable = true; - throw e; - } finally { - if (unlockContextAndDrawable) { - lock.unlock(); - } - } + res = makeCurrentWithinLock(lockRes); + unlockResources = CONTEXT_NOT_CURRENT == res; // success ? + + /** + * 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) { + unlockResources = true; + throw e; + } finally { + if (unlockResources) { + if( DEBUG_TRACE_SWITCH ) { + System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.1]: Context lock.unlock() due to error, res "+makeCurrentResultToString(res)+", "+lock); + } + lock.unlock(); + } + } + } /* if ( drawable.isRealized() ) */ } catch (RuntimeException e) { - unlockContextAndDrawable = true; + unlockResources = true; throw e; } finally { - if (unlockContextAndDrawable) { + if (unlockResources) { drawable.unlockSurface(); - } - } - - if (res == CONTEXT_NOT_CURRENT) { - if(TRACE_SWITCH) { - System.err.println(getThreadName() +": GLContext.ContextSwitch: - switch - CONTEXT_NOT_CURRENT - "+lock); } - } else { + } + + if (res != CONTEXT_NOT_CURRENT) { setCurrent(this); if(res == CONTEXT_CURRENT_NEW) { // check if the drawable's and the GL's GLProfile are equal - // throws an GLException if not + // throws an GLException if not getGLDrawable().getGLProfile().verifyEquality(gl.getGLProfile()); - + glDebugHandler.init( isGL2GL3() && isGLDebugEnabled() ); if(DEBUG_GL) { - gl = gl.getContext().setGL( GLPipelineFactory.create("javax.media.opengl.Debug", null, gl, null) ); + setGL( GLPipelineFactory.create("javax.media.opengl.Debug", null, gl, null) ); if(glDebugHandler.isEnabled()) { glDebugHandler.addListener(new GLDebugMessageHandler.StdErrGLDebugListener(true)); } } if(TRACE_GL) { - gl = gl.getContext().setGL( GLPipelineFactory.create("javax.media.opengl.Trace", null, gl, new Object[] { System.err } ) ); - } - if(DEBUG || TRACE_SWITCH) { - System.err.println(getThreadName() +": GLContext.ContextSwitch: - switch - CONTEXT_CURRENT_NEW - "+lock); + setGL( GLPipelineFactory.create("javax.media.opengl.Trace", null, gl, new Object[] { System.err } ) ); } - } else if(TRACE_SWITCH) { - System.err.println(getThreadName() +": GLContext.ContextSwitch: - switch - CONTEXT_CURRENT - "+lock); + + forceDrawableAssociation = true; } + if( forceDrawableAssociation ) { + associateDrawable(true); + } + + contextMadeCurrent(true); + /* FIXME: refactor dependence on Java 2D / JOGL bridge // Try cleaning up any stale server-side OpenGL objects @@ -465,68 +626,94 @@ public abstract class GLContextImpl extends GLContext { } */ } + if( TRACE_SWITCH ) { + System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.X3]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+" - switch - "+makeCurrentResultToString(res)+" - stateTracker.on "+glStateTracker.isEnabled()+" - "+lock); + } return res; } private final int makeCurrentWithinLock(int surfaceLockRes) throws GLException { if (!isCreated()) { + if( 0 >= drawable.getWidth() || 0 >= drawable.getHeight() ) { + if ( DEBUG_TRACE_SWITCH ) { + System.err.println(getThreadName() + ": Create GL context REJECTED (zero surface size) obj " + toHexString(hashCode()) + ", surf "+toHexString(drawable.getHandle())+" for " + getClass().getName()); + System.err.println(drawable.toString()); + } + return CONTEXT_NOT_CURRENT; + } if(DEBUG_GL) { // only impacts w/ createContextARB(..) additionalCtxCreationFlags |= GLContext.CTX_OPTION_DEBUG ; } - - final GLContextImpl shareWith = (GLContextImpl) GLContextShareSet.getShareContext(this); + + final GLContextImpl shareWith = (GLContextImpl) GLContextShareSet.getCreatedShare(this); + final long shareWithHandle; if (null != shareWith) { shareWith.getDrawableImpl().lockSurface(); + shareWithHandle = shareWith.getHandle(); + if (0 == shareWithHandle) { + throw new GLException("GLContextShareSet returned an invalid OpenGL context: "+this); + } + } else { + shareWithHandle = 0; } final boolean created; try { - created = createImpl(shareWith); // may throws exception if fails! + created = createImpl(shareWithHandle); // may throws exception if fails + if( created && hasNoDefaultVAO() ) { + final int[] tmp = new int[1]; + final GL rootGL = gl.getRootGL(); + if( rootGL.isGL2ES3() ) { // FIXME remove if ES2 == ES3 later + final GL2ES3 gl2es3 = rootGL.getGL2ES3(); + gl2es3.glGenVertexArrays(1, tmp, 0); + defaultVAO = tmp[0]; + gl2es3.glBindVertexArray(defaultVAO); + } + } } finally { if (null != shareWith) { shareWith.getDrawableImpl().unlockSurface(); - } + } } - if (DEBUG) { + if ( DEBUG_TRACE_SWITCH ) { if(created) { - System.err.println(getThreadName() + ": Create GL context OK: " + toHexString(contextHandle) + " for " + getClass().getName()+" - "+getGLVersion()); + System.err.println(getThreadName() + ": Create GL context OK: obj " + toHexString(hashCode()) + ", ctx " + toHexString(contextHandle) + ", surf "+toHexString(drawable.getHandle())+" for " + getClass().getName()+" - "+getGLVersion()); + // Thread.dumpStack(); } else { - System.err.println(getThreadName() + ": Create GL context FAILED for " + getClass().getName()); + System.err.println(getThreadName() + ": Create GL context FAILED obj " + toHexString(hashCode()) + ", surf "+toHexString(drawable.getHandle())+" for " + getClass().getName()); } - } + } if(!created) { return CONTEXT_NOT_CURRENT; } - + // finalize mapping the available GLVersions, in case it's not done yet - { + { final AbstractGraphicsConfiguration config = drawable.getNativeSurface().getGraphicsConfiguration(); final AbstractGraphicsDevice device = config.getScreen().getDevice(); - - if( !GLContext.getAvailableGLVersionsSet(device) ) { - final int reqMajor; - final int reqProfile; - if( 0 != ( ctxOptions & GLContext.CTX_PROFILE_ES) ) { - // ES1 or ES2 - reqMajor = ctxMajorVersion; - reqProfile = GLContext.CTX_PROFILE_ES; - } else { - if(ctxMajorVersion<3 || ctxMajorVersion==3 && ctxMinorVersion==0) { + + // Non ARB desktop profiles may not have been registered + if( !GLContext.getAvailableGLVersionsSet(device) ) { // not yet set + if( 0 == ( ctxOptions & GLContext.CTX_PROFILE_ES) ) { // not ES profile + final int reqMajor; + final int reqProfile; + if( ctxVersion.compareTo(Version300) <= 0 ) { reqMajor = 2; } else { - reqMajor = ctxMajorVersion; + reqMajor = ctxVersion.getMajor(); } if( 0 != ( ctxOptions & GLContext.CTX_PROFILE_CORE) ) { reqProfile = GLContext.CTX_PROFILE_CORE; } else { reqProfile = GLContext.CTX_PROFILE_COMPAT; } - } - GLContext.mapAvailableGLVersion(device, reqMajor, reqProfile, - ctxMajorVersion, ctxMinorVersion, ctxOptions); - GLContext.setAvailableGLVersionsSet(device); - if (DEBUG) { - System.err.println(getThreadName() + ": createContextOLD-MapVersionsAvailable HAVE: " + device+" -> "+reqMajor+"."+reqProfile+ " -> "+getGLVersion()); + GLContext.mapAvailableGLVersion(device, reqMajor, reqProfile, + ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + GLContext.setAvailableGLVersionsSet(device); + + if (DEBUG) { + System.err.println(getThreadName() + ": createContextOLD-MapVersionsAvailable HAVE: " + device+" -> "+reqMajor+"."+reqProfile+ " -> "+getGLVersion()); + } } } } @@ -537,30 +724,44 @@ public abstract class GLContextImpl extends GLContext { return CONTEXT_CURRENT; } protected abstract void makeCurrentImpl() throws GLException; - - /** + + /** + * Calls {@link GLDrawableImpl#associateContext(GLContext, boolean)} + */ + protected void associateDrawable(boolean bound) { + drawable.associateContext(this, bound); + } + + /** + * Calls {@link GLDrawableImpl#contextMadeCurrent(GLContext, boolean)} + */ + protected void contextMadeCurrent(boolean current) { + drawable.contextMadeCurrent(this, current); + } + + /** * Platform dependent entry point for context creation.<br> * * This method is called from {@link #makeCurrentWithinLock()} .. {@link #makeCurrent()} .<br> * - * The implementation shall verify this context with a + * The implementation shall verify this context with a * <code>MakeContextCurrent</code> call.<br> * * The implementation <b>must</b> leave the context current.<br> - * + * * @param share the shared context or null * @return the valid and current context if successful, or null * @throws GLException */ - protected abstract boolean createImpl(GLContextImpl sharedWith) throws GLException ; + protected abstract boolean createImpl(long sharedWithHandle) throws GLException ; - /** + /** * Platform dependent but harmonized implementation of the <code>ARB_create_context</code> * mechanism to create a context.<br> * - * This method is called from {@link #createContextARB}, {@link #createImpl(GLContextImpl)} .. {@link #makeCurrent()} .<br> + * This method is called from {@link #createContextARB}, {@link #createImpl(long)} .. {@link #makeCurrent()} .<br> * - * The implementation shall verify this context with a + * The implementation shall verify this context with a * <code>MakeContextCurrent</code> call.<br> * * The implementation <b>must</b> leave the context current.<br> @@ -582,8 +783,7 @@ public abstract class GLContextImpl extends GLContext { * @see #createContextARBImpl * @see #destroyContextARBImpl */ - protected abstract long createContextARBImpl(long share, boolean direct, int ctxOptionFlags, - int major, int minor); + protected abstract long createContextARBImpl(long share, boolean direct, int ctxOptionFlags, int major, int minor); /** * Destroy the context created by {@link #createContextARBImpl}. @@ -616,13 +816,10 @@ public abstract class GLContextImpl extends GLContext { * @see #createContextARBImpl * @see #destroyContextARBImpl */ - protected final long createContextARB(long share, boolean direct) + protected final long createContextARB(final long share, final boolean direct) { - AbstractGraphicsConfiguration config = drawable.getNativeSurface().getGraphicsConfiguration(); - AbstractGraphicsDevice device = config.getScreen().getDevice(); - GLCapabilitiesImmutable glCaps = (GLCapabilitiesImmutable) config.getChosenCapabilities(); - GLProfile glp = glCaps.getGLProfile(); - GLProfile glpImpl = glp.getImpl(); + final AbstractGraphicsConfiguration config = drawable.getNativeSurface().getGraphicsConfiguration(); + final AbstractGraphicsDevice device = config.getScreen().getDevice(); if (DEBUG) { System.err.println(getThreadName() + ": createContextARB: mappedVersionsAvailableSet("+device.getConnection()+"): "+ @@ -631,64 +828,166 @@ public abstract class GLContextImpl extends GLContext { if ( !GLContext.getAvailableGLVersionsSet(device) ) { if(!mapGLVersions(device)) { - // none of the ARB context creation calls was successful, bail out + // none of the ARB context creation calls was successful, bail out return 0; } } - int reqMajor; - if(glpImpl.isGL4()) { - reqMajor=4; - } else if (glpImpl.isGL3()) { - reqMajor=3; - } else /* if (glpImpl.isGL2()) */ { - reqMajor=2; - } + final GLCapabilitiesImmutable glCaps = (GLCapabilitiesImmutable) config.getChosenCapabilities(); + final int[] reqMajorCTP = new int[] { 0, 0 }; + GLContext.getRequestMajorAndCompat(glCaps.getGLProfile(), reqMajorCTP); - boolean compat = glpImpl.isGL2(); // incl GL3bc and GL4bc + if(DEBUG) { + System.err.println(getThreadName() + ": createContextARB: Requested "+GLContext.getGLVersion(reqMajorCTP[0], 0, reqMajorCTP[0], null)); + } int _major[] = { 0 }; int _minor[] = { 0 }; int _ctp[] = { 0 }; long _ctx = 0; - - if( GLContext.getAvailableGLVersion(device, reqMajor, compat?CTX_PROFILE_COMPAT:CTX_PROFILE_CORE, + if( GLContext.getAvailableGLVersion(device, reqMajorCTP[0], reqMajorCTP[1], _major, _minor, _ctp)) { _ctp[0] |= additionalCtxCreationFlags; + if(DEBUG) { + System.err.println(getThreadName() + ": createContextARB: Mapped "+GLContext.getGLVersion(_major[0], _minor[0], _ctp[0], null)); + } _ctx = createContextARBImpl(share, direct, _ctp[0], _major[0], _minor[0]); if(0!=_ctx) { - setGLFunctionAvailability(true, _major[0], _minor[0], _ctp[0]); + if( !setGLFunctionAvailability(true, _major[0], _minor[0], _ctp[0], false /* strictMatch */, false /* withinGLVersionsMapping */) ) { + throw new InternalError("setGLFunctionAvailability !strictMatch failed"); + } } } return _ctx; } - private final boolean mapGLVersions(AbstractGraphicsDevice device) { + private final boolean mapGLVersions(AbstractGraphicsDevice device) { synchronized (GLContext.deviceVersionAvailable) { + final long t0 = ( DEBUG ) ? System.nanoTime() : 0; boolean success = false; // Following GLProfile.GL_PROFILE_LIST_ALL order of profile detection { GL4bc, GL3bc, GL2, GL4, GL3, GL2GL3, GLES2, GL2ES2, GLES1, GL2ES1 } - success |= createContextARBMapVersionsAvailable(4, true /* compat */); // GL4bc - success |= createContextARBMapVersionsAvailable(3, true /* compat */); // GL3bc - success |= createContextARBMapVersionsAvailable(2, true /* compat */); // GL2 - success |= createContextARBMapVersionsAvailable(4, false /* core */); // GL4 - success |= createContextARBMapVersionsAvailable(3, false /* core */); // GL3 + boolean hasGL4bc = false; + boolean hasGL3bc = false; + boolean hasGL2 = false; + boolean hasGL4 = false; + boolean hasGL3 = false; + + // Even w/ PROFILE_ALIASING, try to use true core GL profiles + // ensuring proper user behavior across platforms due to different feature sets! + // + if( Platform.OSType.MACOS == Platform.getOSType() && + Platform.getOSVersionNumber().compareTo(Platform.OSXVersion.Mavericks) >= 0 ) { + /** + * OSX 10.9 GLRendererQuirks.GL4NeedsGL3Request, quirk is added as usual @ setRendererQuirks(..) + */ + if( !hasGL4 && !hasGL3 ) { + hasGL3 = createContextARBMapVersionsAvailable(3, CTX_PROFILE_CORE); // GL3 + success |= hasGL3; + if( hasGL3 ) { + final boolean isHWAccel = 0 == ( CTX_IMPL_ACCEL_SOFT & ctxOptions ); + if( isHWAccel && ctxVersion.getMajor() >= 4 ) { + // Gotcha: Creating a '3.2' ctx delivers a >= 4 ctx. + GLContext.mapAvailableGLVersion(device, 4, CTX_PROFILE_CORE, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + hasGL4 = true; + if(DEBUG) { + System.err.println("Quirk Triggerd: "+GLRendererQuirks.toString(GLRendererQuirks.GL4NeedsGL3Request)+": cause: OS "+Platform.getOSType()+", OS Version "+Platform.getOSVersionNumber()); + } + } + resetStates(false); // clean this context states, since creation was temporary + } + } + } + if( !hasGL4 ) { + hasGL4 = createContextARBMapVersionsAvailable(4, CTX_PROFILE_CORE); // GL4 + success |= hasGL4; + if( hasGL4 ) { + if( 0 == ( CTX_IMPL_ACCEL_SOFT & ctxOptions ) ) { + // Map hw-accel GL4 to all lower core profiles: GL3 + GLContext.mapAvailableGLVersion(device, 3, CTX_PROFILE_CORE, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + if( PROFILE_ALIASING ) { + hasGL3 = true; + } + } + resetStates(false); // clean context states, since creation was temporary + } + } + if( !hasGL3 ) { + hasGL3 = createContextARBMapVersionsAvailable(3, CTX_PROFILE_CORE); // GL3 + success |= hasGL3; + if( hasGL3 ) { + resetStates(false); // clean this context states, since creation was temporary + } + } + if( !hasGL4bc ) { + hasGL4bc = createContextARBMapVersionsAvailable(4, CTX_PROFILE_COMPAT); // GL4bc + success |= hasGL4bc; + if( hasGL4bc ) { + if( !hasGL4 ) { // last chance .. ignore hw-accel + GLContext.mapAvailableGLVersion(device, 4, CTX_PROFILE_CORE, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + hasGL4 = true; + } + if( !hasGL3 ) { // last chance .. ignore hw-accel + GLContext.mapAvailableGLVersion(device, 3, CTX_PROFILE_CORE, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + hasGL3 = true; + } + if( 0 == ( CTX_IMPL_ACCEL_SOFT & ctxOptions ) ) { + // Map hw-accel GL4bc to all lower compatible profiles: GL3bc, GL2 + GLContext.mapAvailableGLVersion(device, 3, CTX_PROFILE_COMPAT, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + GLContext.mapAvailableGLVersion(device, 2, CTX_PROFILE_COMPAT, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + if(PROFILE_ALIASING) { + hasGL3bc = true; + hasGL2 = true; + } + } + resetStates(false); // clean this context states, since creation was temporary + } + } + if( !hasGL3bc ) { + hasGL3bc = createContextARBMapVersionsAvailable(3, CTX_PROFILE_COMPAT); // GL3bc + success |= hasGL3bc; + if( hasGL3bc ) { + if(!hasGL3) { // last chance .. ignore hw-accel + GLContext.mapAvailableGLVersion(device, 3, CTX_PROFILE_CORE, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + hasGL3 = true; + } + if( 0 == ( CTX_IMPL_ACCEL_SOFT & ctxOptions ) ) { + // Map hw-accel GL3bc to all lower compatible profiles: GL2 + GLContext.mapAvailableGLVersion(device, 2, CTX_PROFILE_COMPAT, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + if(PROFILE_ALIASING) { + hasGL2 = true; + } + } + resetStates(false); // clean this context states, since creation was temporary + } + } + if( !hasGL2 ) { + hasGL2 = createContextARBMapVersionsAvailable(2, CTX_PROFILE_COMPAT); // GL2 + success |= hasGL2; + if( hasGL2 ) { + resetStates(false); // clean this context states, since creation was temporary + } + } if(success) { // only claim GL versions set [and hence detected] if ARB context creation was successful GLContext.setAvailableGLVersionsSet(device); + if(DEBUG) { + final long t1 = System.nanoTime(); + System.err.println("GLContextImpl.mapGLVersions: "+device+", profileAliasing: "+PROFILE_ALIASING+", total "+(t1-t0)/1e6 +"ms"); + System.err.println(GLContext.dumpAvailableGLVersions(null).toString()); + } } else if (DEBUG) { System.err.println(getThreadName() + ": createContextARB-MapVersions NONE for :"+device); - } + } return success; } } - private final boolean createContextARBMapVersionsAvailable(int reqMajor, boolean compat) { + /** + * Note: Since context creation is temporary, caller need to issue {@link #resetStates(boolean)}, if creation was successful, i.e. returns true. + * This method does not reset the states, allowing the caller to utilize the state variables. + **/ + private final boolean createContextARBMapVersionsAvailable(int reqMajor, int reqProfile) { long _context; - int reqProfile = compat ? CTX_PROFILE_COMPAT : CTX_PROFILE_CORE ; - int ctp = CTX_IS_ARB_CREATED | CTX_PROFILE_CORE; // default - if(compat) { - ctp &= ~CTX_PROFILE_CORE ; - ctp |= CTX_PROFILE_COMPAT ; - } + int ctp = CTX_IS_ARB_CREATED | reqProfile; // To ensure GL profile compatibility within the JOGL application // we always try to map against the highest GL version, @@ -698,104 +997,93 @@ public abstract class GLContextImpl extends GLContext { int major[] = new int[1]; int minor[] = new int[1]; if( 4 == reqMajor ) { - majorMax=4; minorMax=GLContext.getMaxMinor(majorMax); + majorMax=4; minorMax=GLContext.getMaxMinor(ctp, majorMax); majorMin=4; minorMin=0; } else if( 3 == reqMajor ) { - majorMax=3; minorMax=GLContext.getMaxMinor(majorMax); + majorMax=3; minorMax=GLContext.getMaxMinor(ctp, majorMax); majorMin=3; minorMin=1; } else /* if( glp.isGL2() ) */ { - // our minimum desktop OpenGL runtime requirements are 1.1, - // nevertheless we restrict ARB context creation to 2.0 to spare us futile attempts + // our minimum desktop OpenGL runtime requirements are 1.1, + // nevertheless we restrict ARB context creation to 2.0 to spare us futile attempts majorMax=3; minorMax=0; majorMin=2; minorMin=0; } - _context = createContextARBVersions(0, true, ctp, + _context = createContextARBVersions(0, true, ctp, /* max */ majorMax, minorMax, /* min */ majorMin, minorMin, /* res */ major, minor); - if(0==_context && !compat) { + if( 0 == _context && CTX_PROFILE_CORE == reqProfile && !PROFILE_ALIASING ) { // try w/ FORWARD instead of CORE ctp &= ~CTX_PROFILE_CORE ; ctp |= CTX_OPTION_FORWARD ; - _context = createContextARBVersions(0, true, ctp, + _context = createContextARBVersions(0, true, ctp, /* max */ majorMax, minorMax, /* min */ majorMin, minorMin, /* res */ major, minor); - if(0==_context) { + if( 0 == _context ) { // Try a compatible one .. even though not requested .. last resort ctp &= ~CTX_PROFILE_CORE ; ctp &= ~CTX_OPTION_FORWARD ; ctp |= CTX_PROFILE_COMPAT ; - _context = createContextARBVersions(0, true, ctp, + _context = createContextARBVersions(0, true, ctp, /* max */ majorMax, minorMax, /* min */ majorMin, minorMin, /* res */ major, minor); } } - if(0!=_context) { - AbstractGraphicsDevice device = drawable.getNativeSurface().getGraphicsConfiguration().getScreen().getDevice(); - // ctxMajorVersion, ctxMinorVersion, ctxOptions is being set by + final boolean res; + if( 0 != _context ) { + AbstractGraphicsDevice device = drawable.getNativeSurface().getGraphicsConfiguration().getScreen().getDevice(); + // ctxMajorVersion, ctxMinorVersion, ctxOptions is being set by // createContextARBVersions(..) -> setGLFunctionAvailbility(..) -> setContextVersion(..) - GLContext.mapAvailableGLVersion(device, reqMajor, reqProfile, ctxMajorVersion, ctxMinorVersion, ctxOptions); + GLContext.mapAvailableGLVersion(device, reqMajor, reqProfile, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); destroyContextARBImpl(_context); if (DEBUG) { System.err.println(getThreadName() + ": createContextARB-MapVersionsAvailable HAVE: " +reqMajor+"."+reqProfile+ " -> "+getGLVersion()); } - // only reset [and hence modify] this context state if ARB context creation was successful - resetStates(); - return true; + res = true; } else { if (DEBUG) { System.err.println(getThreadName() + ": createContextARB-MapVersionsAvailable NOPE: "+reqMajor+"."+reqProfile); - } - return false; + } + res = false; } + return res; } - private final long createContextARBVersions(long share, boolean direct, int ctxOptionFlags, - int majorMax, int minorMax, - int majorMin, int minorMin, + private final long createContextARBVersions(long share, boolean direct, int ctxOptionFlags, + int majorMax, int minorMax, + int majorMin, int minorMin, int major[], int minor[]) { major[0]=majorMax; minor[0]=minorMax; long _context=0; - boolean ok = false; - - while ( !ok && - GLContext.isValidGLVersion(major[0], minor[0]) && - ( major[0]>majorMin || major[0]==majorMin && minor[0] >=minorMin ) ) { + int i=0; + do { if (DEBUG) { - System.err.println(getThreadName() + ": createContextARBVersions: share "+share+", direct "+direct+", version "+major[0]+"."+minor[0]); + i++; + System.err.println(getThreadName() + ": createContextARBVersions."+i+": share "+share+", direct "+direct+ + ", version "+major[0]+"."+minor[0]+", major["+majorMin+".."+majorMax+"], minor["+minorMin+".."+minorMax+"]"); } _context = createContextARBImpl(share, direct, ctxOptionFlags, major[0], minor[0]); if(0 != _context) { - ok = true; - setGLFunctionAvailability(true, major[0], minor[0], ctxOptionFlags); - } else { - ok = false; - } - - if(ok && major[0]>=3) { - int[] hasMajor = new int[1]; int[] hasMinor = new int[1]; - gl.glGetIntegerv(GL3.GL_MAJOR_VERSION, hasMajor, 0); - gl.glGetIntegerv(GL3.GL_MINOR_VERSION, hasMinor, 0); - ok = hasMajor[0]>major[0] || ( hasMajor[0]==major[0] && hasMinor[0]>=minor[0] ) ; - if(!ok) { - removeCachedVersion(major[0], minor[0], ctxOptionFlags); + if( setGLFunctionAvailability(true, major[0], minor[0], ctxOptionFlags, true /* strictMatch */, true /* withinGLVersionsMapping */) ) { + break; + } else { destroyContextARBImpl(_context); _context = 0; } - if (DEBUG) { - System.err.println(getThreadName() + ": createContextARBVersions: version verification - expected "+major[0]+"."+minor[0]+", has "+hasMajor[0]+"."+hasMinor[0]+" == "+ok); - } - } - - if(!ok) { - if(!GLContext.decrementGLVersion(major, minor)) break; } + + } while ( ( major[0]>majorMin || major[0]==majorMin && minor[0] >minorMin ) && // #1 check whether version is above lower limit + GLContext.decrementGLVersion(ctxOptionFlags, major, minor) // #2 decrement version + ); + if (DEBUG) { + System.err.println(getThreadName() + ": createContextARBVersions.X: ctx "+toHexString(_context)+", share "+share+", direct "+direct+ + ", version "+major[0]+"."+minor[0]+", major["+majorMin+".."+majorMax+"], minor["+minorMin+".."+minorMax+"]"); } return _context; } @@ -805,52 +1093,31 @@ public abstract class GLContextImpl extends GLContext { // As a last resort, the GL_VERSION string may be used .. // - /** - * If major > 0 || minor > 0 : Use passed values, determined at creation time - * If major==0 && minor == 0 : Use GL_VERSION + /** + * If major > 0 || minor > 0 : Use passed values, determined at creation time * Otherwise .. don't touch .. */ - private final void setContextVersion(int major, int minor, int ctp, boolean setVersionString) { - if (0==ctp) { + private final void setContextVersion(int major, int minor, int ctp, VersionNumberString glVendorVersion, boolean useGL) { + if ( 0 == ctp ) { throw new GLException("Invalid GL Version "+major+"."+minor+", ctp "+toHexString(ctp)); } - if(major>0 || minor>0) { - if (!GLContext.isValidGLVersion(major, minor)) { - GLException e = new GLException("Invalid GL Version "+major+"."+minor+", ctp "+toHexString(ctp)); - throw e; - } - ctxMajorVersion = major; - ctxMinorVersion = minor; - ctxOptions = ctp; - if(setVersionString) { - ctxVersionString = getGLVersion(ctxMajorVersion, ctxMinorVersion, ctxOptions, getGL().glGetString(GL.GL_VERSION)); - } - return; - } - - if(major==0 && minor==0) { - String versionStr = getGL().glGetString(GL.GL_VERSION); - if(null==versionStr) { - throw new GLException("GL_VERSION is NULL: "+this); - } - ctxOptions = ctp; - - // Set version - GLVersionNumber version = new GLVersionNumber(versionStr); - if (version.isValid()) { - ctxMajorVersion = version.getMajor(); - ctxMinorVersion = version.getMinor(); - // We cannot promote a non ARB context to >= 3.1, - // reduce it to 3.0 then. - if ( ( ctxMajorVersion>3 || ctxMajorVersion==3 && ctxMinorVersion>=1 ) - && 0 == (ctxOptions & CTX_IS_ARB_CREATED) ) { - ctxMajorVersion = 3; - ctxMinorVersion = 0; - } - if(setVersionString) { - ctxVersionString = getGLVersion(ctxMajorVersion, ctxMinorVersion, ctxOptions, versionStr); - } - return; + ctxVersion = new VersionNumber(major, minor, 0); + ctxVersionString = getGLVersion(major, minor, ctp, glVersion); + ctxVendorVersion = glVendorVersion; + ctxOptions = ctp; + if(useGL) { + ctxGLSLVersion = VersionNumber.zeroVersion; + if( hasGLSL() ) { // >= ES2 || GL2.0 + final String glslVersion = isGLES() ? null : gl.glGetString(GL2ES2.GL_SHADING_LANGUAGE_VERSION) ; // Use static GLSL version for ES to be safe! + if( null != glslVersion ) { + ctxGLSLVersion = new VersionNumber(glslVersion); + if( ctxGLSLVersion.getMajor() < 1 ) { + ctxGLSLVersion = VersionNumber.zeroVersion; // failed .. + } + } + if( ctxGLSLVersion.isZero() ) { + ctxGLSLVersion = getStaticGLSLVersionNumber(major, minor, ctxOptions); + } } } } @@ -859,18 +1126,18 @@ public abstract class GLContextImpl extends GLContext { // Helpers for various context implementations // - private Object createInstance(GLProfile glp, String suffix, Class<?>[] cstrArgTypes, Object[] cstrArgs) { - return ReflectionUtil.createInstance(glp.getGLImplBaseClassName()+suffix, cstrArgTypes, cstrArgs, getClass().getClassLoader()); + private Object createInstance(GLProfile glp, boolean glObject, Object[] cstrArgs) { + return ReflectionUtil.createInstance(glp.getGLCtor(glObject), cstrArgs); } private boolean verifyInstance(GLProfile glp, String suffix, Object instance) { - return ReflectionUtil.instanceOf(instance, glp.getGLImplBaseClassName()+suffix); + return ReflectionUtil.instanceOf(instance, glp.getGLImplBaseClassName()+suffix); } /** Create the GL for this context. */ protected GL createGL(GLProfile glp) { - GL gl = (GL) createInstance(glp, "Impl", new Class[] { GLProfile.class, GLContextImpl.class }, new Object[] { glp, this } ); - + final GL gl = (GL) createInstance(glp, true, new Object[] { glp, this } ); + /* FIXME: refactor dependence on Java 2D / JOGL bridge if (tracker != null) { gl.setObjectTracker(tracker); @@ -878,11 +1145,32 @@ public abstract class GLContextImpl extends GLContext { */ return gl; } - + + /** + * Finalizes GL instance initialization after this context has been initialized. + * <p> + * Method calls 'void finalizeInit()' of instance 'gl' as retrieved by reflection, if exist. + * </p> + */ + private void finalizeInit(GL gl) { + Method finalizeInit = null; + try { + finalizeInit = ReflectionUtil.getMethod(gl.getClass(), "finalizeInit", new Class<?>[]{ }); + } catch ( Throwable t ) { + if(DEBUG) { + System.err.println("Catched "+t.getClass().getName()+": "+t.getMessage()); + t.printStackTrace(); + } + } + if( null != finalizeInit ) { + ReflectionUtil.callMethod(gl, finalizeInit, new Object[]{ }); + } + } + public final ProcAddressTable getGLProcAddressTable() { return glProcAddressTable; } - + /** * Shall return the platform extension ProcAddressTable, * ie for GLXExt, EGLExt, .. @@ -890,18 +1178,22 @@ public abstract class GLContextImpl extends GLContext { public abstract ProcAddressTable getPlatformExtProcAddressTable(); /** - * Pbuffer support; given that this is a GLContext associated with a - * pbuffer, binds this pbuffer to its texture target. + * Part of <code>GL_NV_vertex_array_range</code>. + * <p> + * Provides platform-independent access to the <code>wglAllocateMemoryNV</code> / + * <code>glXAllocateMemoryNV</code>. + * </p> */ - public abstract void bindPbufferToTexture(); + public abstract ByteBuffer glAllocateMemoryNV(int size, float readFrequency, float writeFrequency, float priority); /** - * Pbuffer support; given that this is a GLContext associated with a - * pbuffer, releases this pbuffer from its texture target. + * Part of <code>GL_NV_vertex_array_range</code>. + * <p> + * Provides platform-independent access to the <code>wglFreeMemoryNV</code> / + * <code>glXFreeMemoryNV</code>. + * </p> */ - public abstract void releasePbufferFromTexture(); - - public abstract ByteBuffer glAllocateMemoryNV(int arg0, float arg1, float arg2, float arg3); + public abstract void glFreeMemoryNV(ByteBuffer pointer); /** Maps the given "platform-independent" function name to a real function name. Currently this is only used to map "glAllocateMemoryNV" and @@ -918,8 +1210,8 @@ public abstract class GLContextImpl extends GLContext { /** Maps the given "platform-independent" extension name to a real function name. Currently this is only used to map - "GL_ARB_pbuffer" to "WGL_ARB_pbuffer/GLX_SGIX_pbuffer" and - "GL_ARB_pixel_format" to "WGL_ARB_pixel_format/n.a." + "GL_ARB_pbuffer" to "WGL_ARB_pbuffer/GLX_SGIX_pbuffer" and + "GL_ARB_pixel_format" to "WGL_ARB_pixel_format/n.a." */ protected final String mapToRealGLExtensionName(String glExtensionName) { Map<String, String> map = getExtensionNameMap(); @@ -933,41 +1225,118 @@ public abstract class GLContextImpl extends GLContext { /** Helper routine which resets a ProcAddressTable generated by the GLEmitter by looking up anew all of its function pointers. */ - protected final void resetProcAddressTable(ProcAddressTable table) { - table.reset(getDrawableImpl().getGLDynamicLookupHelper() ); + protected final void resetProcAddressTable(final ProcAddressTable table) { + AccessController.doPrivileged(new PrivilegedAction<Object>() { + @Override + public Object run() { + table.reset(getDrawableImpl().getGLDynamicLookupHelper() ); + return null; + } + } ); } - private final void initGLRendererStrings() { + private final boolean initGLRendererAndGLVersionStrings() { final GLDynamicLookupHelper glDynLookupHelper = getDrawableImpl().getGLDynamicLookupHelper(); final long _glGetString = glDynLookupHelper.dynamicLookupFunction("glGetString"); if(0 == _glGetString) { - // FIXME - System.err.println("Warning: Entry point to 'glGetString' is NULL."); - Thread.dumpStack(); + System.err.println("Error: Entry point to 'glGetString' is NULL."); + if(DEBUG) { + Thread.dumpStack(); + } + return false; } else { + final String _glVendor = glGetStringInt(GL.GL_VENDOR, _glGetString); + if(null == _glVendor) { + if(DEBUG) { + System.err.println("Warning: GL_VENDOR is NULL."); + Thread.dumpStack(); + } + return false; + } + glVendor = _glVendor; + final String _glRenderer = glGetStringInt(GL.GL_RENDERER, _glGetString); if(null == _glRenderer) { + if(DEBUG) { + System.err.println("Warning: GL_RENDERER is NULL."); + Thread.dumpStack(); + } + return false; + } + glRenderer = _glRenderer; + glRendererLowerCase = glRenderer.toLowerCase(); + + final String _glVersion = glGetStringInt(GL.GL_VERSION, _glGetString); + if(null == _glVersion) { // FIXME - System.err.println("Warning: GL_RENDERER is NULL."); - Thread.dumpStack(); - } else { - glRenderer = _glRenderer; - glRendererLowerCase = glRenderer.toLowerCase(); + if(DEBUG) { + System.err.println("Warning: GL_VERSION is NULL."); + Thread.dumpStack(); + } + return false; + } + glVersion = _glVersion; + + return true; + } + } + + /** + * Returns null if version string is invalid, otherwise a valid instance. + * <p> + * Note: Non ARB ctx is limited to GL 3.0. + * </p> + */ + private static final VersionNumber getGLVersionNumber(int ctp, String glVersionStr) { + if( null != glVersionStr ) { + final GLVersionNumber version = GLVersionNumber.create(glVersionStr); + if ( version.isValid() ) { + int[] major = new int[] { version.getMajor() }; + int[] minor = new int[] { version.getMinor() }; + if ( GLContext.isValidGLVersion(ctp, major[0], minor[0]) ) { + return new VersionNumber(major[0], minor[0], 0); + } + } + } + return null; + } + + /** + * Returns false if <code>glGetIntegerv</code> is inaccessible, otherwise queries major.minor + * version for given arrays. + * <p> + * If the GL query fails, major will be zero. + * </p> + */ + private final boolean getGLIntVersion(int[] glIntMajor, int[] glIntMinor) { + glIntMajor[0] = 0; // clear + final GLDynamicLookupHelper glDynLookupHelper = getDrawableImpl().getGLDynamicLookupHelper(); + final long _glGetIntegerv = glDynLookupHelper.dynamicLookupFunction("glGetIntegerv"); + if( 0 == _glGetIntegerv ) { + System.err.println("Error: Entry point to 'glGetIntegerv' is NULL."); + if(DEBUG) { + Thread.dumpStack(); } + return false; + } else { + glGetIntegervInt(GL2GL3.GL_MAJOR_VERSION, glIntMajor, 0, _glGetIntegerv); + glGetIntegervInt(GL2GL3.GL_MINOR_VERSION, glIntMinor, 0, _glGetIntegerv); + return true; } } - - protected final String getGLRendererString(boolean lowerCase) { - return lowerCase ? glRendererLowerCase : glRenderer; + + protected final int getCtxOptions() { + return ctxOptions; } - + + /** * Sets the OpenGL implementation class and * the cache of which GL functions are available for calling through this * context. See {@link #isFunctionAvailable(String)} for more information on * the definition of "available". * <br> - * All ProcaddressTables are being determined, the GL version is being set + * All ProcaddressTables are being determined and cached, the GL version is being set * and the extension cache is determined as well. * * @param force force the setting, even if is already being set. @@ -975,33 +1344,227 @@ public abstract class GLContextImpl extends GLContext { * @param major OpenGL major version * @param minor OpenGL minor version * @param ctxProfileBits OpenGL context profile and option bits, see {@link javax.media.opengl.GLContext#CTX_OPTION_ANY} - * + * @param strictMatch if <code>true</code> the ctx must + * <ul> + * <li>be greater or equal than the requested <code>major.minor</code> version, and</li> + * <li>match the ctxProfileBits</li> + * <li>match ES major versions</li> + * </ul>, otherwise method aborts and returns <code>false</code>.<br> + * if <code>false</code> no version check is performed. + * @param withinGLVersionsMapping if <code>true</code> GL version mapping is in process, i.e. quering avail versions. + * Otherwise normal user context creation. + * @return returns <code>true</code> if successful, otherwise <code>false</code>.<br> + * If <code>strictMatch</code> is <code>false</code> method shall always return <code>true</code> or throw an exception. + * If <code>false</code> is returned, no data has been cached or mapped, i.e. ProcAddressTable, Extensions, Version, etc. * @see #setContextVersion * @see javax.media.opengl.GLContext#CTX_OPTION_ANY * @see javax.media.opengl.GLContext#CTX_PROFILE_COMPAT * @see javax.media.opengl.GLContext#CTX_IMPL_ES2_COMPAT */ - protected final void setGLFunctionAvailability(boolean force, int major, int minor, int ctxProfileBits) { + protected final boolean setGLFunctionAvailability(boolean force, int major, int minor, int ctxProfileBits, + boolean strictMatch, boolean withinGLVersionsMapping) { if(null!=this.gl && null!=glProcAddressTable && !force) { - return; // already done and not forced + return true; // already done and not forced + } + + if ( 0 < major && !GLContext.isValidGLVersion(ctxProfileBits, major, minor) ) { + throw new GLException("Invalid GL Version Request "+GLContext.getGLVersion(major, minor, ctxProfileBits, null)); } if(null==this.gl || !verifyInstance(gl.getGLProfile(), "Impl", this.gl)) { - setGL(createGL(getGLDrawable().getGLProfile())); + setGL( createGL( getGLDrawable().getGLProfile() ) ); } updateGLXProcAddressTable(); - initGLRendererStrings(); - - if(!isCurrentContextHardwareRasterizer()) { - ctxProfileBits |= GLContext.CTX_IMPL_ACCEL_SOFT; - } final AbstractGraphicsConfiguration aconfig = drawable.getNativeSurface().getGraphicsConfiguration(); final AbstractGraphicsDevice adevice = aconfig.getScreen().getDevice(); - + final int reqCtxProfileBits = ctxProfileBits; + final VersionNumber reqGLVersion = new VersionNumber(major, minor, 0); + final VersionNumber hasGLVersionByString; + { + final boolean initGLRendererAndGLVersionStringsOK = initGLRendererAndGLVersionStrings(); + if( !initGLRendererAndGLVersionStringsOK ) { + final String errMsg = "Intialization of GL renderer strings failed. "+adevice+" - "+GLContext.getGLVersion(major, minor, ctxProfileBits, null); + if( strictMatch ) { + // query mode .. simply fail + if(DEBUG) { + System.err.println("Warning: setGLFunctionAvailability: "+errMsg); + } + return false; + } else { + // unusable GL context - non query mode - hard fail! + throw new GLException(errMsg); + } + } else { + hasGLVersionByString = getGLVersionNumber(ctxProfileBits, glVersion); + if(DEBUG) { + System.err.println(getThreadName() + ": GLContext.setGLFuncAvail: Given "+adevice+ + " - "+GLContext.getGLVersion(major, minor, ctxProfileBits, glVersion)+ + ", Number(Str) "+hasGLVersionByString); + } + } + } + + final boolean isES = 0 != ( CTX_PROFILE_ES & ctxProfileBits ); + + // + // Validate GL version either by GL-Integer or GL-String + // + if (DEBUG) { + System.err.println(getThreadName() + ": GLContext.setGLFuncAvail: Pre version verification - expected "+GLContext.getGLVersion(major, minor, ctxProfileBits, null)+", strictMatch "+strictMatch+", glVersionsMapping " +withinGLVersionsMapping); + } + + final boolean versionGL3IntOK; + { + // Validate the requested version w/ the GL-version from an integer query, + // as supported by GL [ES] >= 3.0 implementation. + final VersionNumber hasGLVersionByInt; + { + final int[] glIntMajor = new int[] { 0 }, glIntMinor = new int[] { 0 }; + final boolean getGLIntVersionOK = getGLIntVersion(glIntMajor, glIntMinor); + if( !getGLIntVersionOK ) { + final String errMsg = "Fetching GL Integer Version failed. "+adevice+" - "+GLContext.getGLVersion(major, minor, ctxProfileBits, null); + if( strictMatch ) { + // query mode .. simply fail + if(DEBUG) { + System.err.println("Warning: setGLFunctionAvailability: "+errMsg); + } + return false; + } else { + // unusable GL context - non query mode - hard fail! + throw new GLException(errMsg); + } + } + hasGLVersionByInt = new VersionNumber(glIntMajor[0], glIntMinor[0], 0); + } + if (DEBUG) { + System.err.println(getThreadName() + ": GLContext.setGLFuncAvail: Version verification (Int): String "+glVersion+", Number(Int) "+hasGLVersionByInt); + } + + // Only validate integer based version if: + // - ctx >= 3.0 is requested _or_ string-version >= 3.0 + // - _and_ a valid int version was fetched, + // otherwise cont. w/ version-string method -> 3.0 > Version || Version > MAX! + // + if ( ( major >= 3 || hasGLVersionByString.compareTo(Version300) >= 0 ) && + GLContext.isValidGLVersion(ctxProfileBits, hasGLVersionByInt.getMajor(), hasGLVersionByInt.getMinor()) ) { + // Strict Match (GLVersionMapping): + // Relaxed match for versions ( !isES && major < 3 ) requests, last resort! + // Otherwise: + // - fail if hasVersion < reqVersion (desktop and ES) + // - fail if ES major-version mismatch: + // - request 1, >= 3 must be equal + // - request 2 must be [2..3] + // + final int hasMajor = hasGLVersionByInt.getMajor(); + if( strictMatch && + ( ( ( isES || major >= 3 ) && hasGLVersionByInt.compareTo(reqGLVersion) < 0 ) || + ( isES && + ( + ( 2 == major && ( 2 > hasMajor || hasMajor > 3 ) ) || // 2 -> [2..3] + ( ( 1 == major || 3 <= major ) && major != hasMajor ) // 1,3,.. -> equal + ) + ) + ) ) { + if(DEBUG) { + System.err.println(getThreadName() + ": GLContext.setGLFuncAvail.X: FAIL, GL version mismatch (Int): "+GLContext.getGLVersion(major, minor, ctxProfileBits, null)+" -> "+glVersion+", "+hasGLVersionByInt); + } + return false; + } + // Use returned GL version! + major = hasGLVersionByInt.getMajor(); + minor = hasGLVersionByInt.getMinor(); + versionGL3IntOK = true; + } else { + versionGL3IntOK = false; + } + } + final boolean versionValidated; + + if( versionGL3IntOK ) { + versionValidated = true; + } else { + // Validate the requested version w/ the GL-version from the version string. + if (DEBUG) { + System.err.println(getThreadName() + ": GLContext.setGLFuncAvail: Version verification (String): String "+glVersion+", Number(Str) "+hasGLVersionByString); + } + + // Only validate if a valid string version was fetched -> MIN > Version || Version > MAX! + if( null != hasGLVersionByString ) { + // Strict Match (GLVersionMapping): + // Relaxed match for versions ( !isES && major < 3 ) requests, last resort! + // Otherwise: + // - fail if hasVersion < reqVersion (desktop and ES) + // - fail if ES major-version mismatch: + // - request 1, >= 3 must be equal + // - request 2 must be [2..3] + // + final int hasMajor = hasGLVersionByString.getMajor(); + if( strictMatch && + ( ( ( isES || major >= 3 ) && hasGLVersionByString.compareTo(reqGLVersion) < 0 ) || + ( isES && + ( + ( 2 == major && ( 2 > hasMajor || hasMajor > 3 ) ) || // 2 -> [2..3] + ( ( 1 == major || 3 <= major ) && major != hasMajor ) // 1,3,.. -> equal + ) + ) + ) ) { + if(DEBUG) { + System.err.println(getThreadName() + ": GLContext.setGLFuncAvail.X: FAIL, GL version mismatch (String): "+GLContext.getGLVersion(major, minor, ctxProfileBits, null)+" -> "+glVersion+", "+hasGLVersionByString); + } + return false; + } + if( strictMatch && !versionGL3IntOK && major >= 3 ) { + if(DEBUG) { + System.err.println(getThreadName() + ": GLContext.setGLFuncAvail.X: FAIL, GL3/ES3 version Int failed, String: "+GLContext.getGLVersion(major, minor, ctxProfileBits, null)+" -> "+glVersion+", "+hasGLVersionByString); + } + return false; + } + // Use returned GL version! + major = hasGLVersionByString.getMajor(); + minor = hasGLVersionByString.getMinor(); + versionValidated = true; + } else { + versionValidated = false; + } + } + if( strictMatch && !versionValidated ) { + if(DEBUG) { + System.err.println(getThreadName() + ": GLContext.setGLFuncAvail.X: FAIL, No GL version validation possible: "+GLContext.getGLVersion(major, minor, ctxProfileBits, null)+" -> "+glVersion); + } + return false; + } + if (DEBUG) { + System.err.println(getThreadName() + ": GLContext.setGLFuncAvail: Post version verification req "+ + GLContext.getGLVersion(reqGLVersion.getMajor(), reqGLVersion.getMinor(), reqCtxProfileBits, null)+" -> has "+ + GLContext.getGLVersion(major, minor, ctxProfileBits, null)+ + ", strictMatch "+strictMatch+", versionValidated "+versionValidated+", versionGL3IntOK "+versionGL3IntOK); + } + + if( major < 2 ) { // there is no ES2/3-compat for a profile w/ major < 2 + ctxProfileBits &= ~ ( GLContext.CTX_IMPL_ES2_COMPAT | GLContext.CTX_IMPL_ES3_COMPAT ) ; + } + + final VersionNumberString vendorVersion = GLVersionNumber.createVendorVersion(glVersion); + + setRendererQuirks(adevice, getDrawableImpl().getFactoryImpl(), + reqGLVersion.getMajor(), reqGLVersion.getMinor(), reqCtxProfileBits, + major, minor, ctxProfileBits, vendorVersion, withinGLVersionsMapping); + + if( strictMatch && glRendererQuirks.exist(GLRendererQuirks.GLNonCompliant) ) { + if(DEBUG) { + System.err.println(getThreadName() + ": GLContext.setGLFuncAvail.X: FAIL, GL is not compliant: "+GLContext.getGLVersion(major, minor, ctxProfileBits, glVersion)+", "+glRenderer); + } + return false; + } + + if(!isCurrentContextHardwareRasterizer()) { + ctxProfileBits |= GLContext.CTX_IMPL_ACCEL_SOFT; + } + contextFQN = getContextFQN(adevice, major, minor, ctxProfileBits); if (DEBUG) { - System.err.println(getThreadName() + ": Context FQN: "+contextFQN+" - "+GLContext.getGLVersion(major, minor, ctxProfileBits, null)); + System.err.println(getThreadName() + ": GLContext.setGLFuncAvail.0 validated FQN: "+contextFQN+" - "+GLContext.getGLVersion(major, minor, ctxProfileBits, glVersion)); } // @@ -1011,7 +1574,7 @@ public abstract class GLContextImpl extends GLContext { synchronized(mappedContextTypeObjectLock) { table = mappedGLProcAddress.get( contextFQN ); if(null != table && !verifyInstance(gl.getGLProfile(), "ProcAddressTable", table)) { - throw new InternalError("GLContext GL ProcAddressTable mapped key("+contextFQN+" - " + GLContext.getGLVersion(major, minor, ctxProfileBits, null)+ + throw new InternalError("GLContext GL ProcAddressTable mapped key("+contextFQN+" - " + GLContext.getGLVersion(major, minor, ctxProfileBits, null)+ ") -> "+ table.getClass().getName()+" not matching "+gl.getGLProfile().getGLImplBaseClassName()); } } @@ -1021,8 +1584,7 @@ public abstract class GLContextImpl extends GLContext { System.err.println(getThreadName() + ": GLContext GL ProcAddressTable reusing key("+contextFQN+") -> "+toHexString(table.hashCode())); } } else { - glProcAddressTable = (ProcAddressTable) createInstance(gl.getGLProfile(), "ProcAddressTable", - new Class[] { FunctionAddressResolver.class } , + glProcAddressTable = (ProcAddressTable) createInstance(gl.getGLProfile(), false, new Object[] { new GLProcAddressResolver() } ); resetProcAddressTable(getGLProcAddressTable()); synchronized(mappedContextTypeObjectLock) { @@ -1032,7 +1594,7 @@ public abstract class GLContextImpl extends GLContext { } } } - + // // Update ExtensionAvailabilityCache // @@ -1047,7 +1609,7 @@ public abstract class GLContextImpl extends GLContext { } } else { extensionAvailability = new ExtensionAvailabilityCache(); - setContextVersion(major, minor, ctxProfileBits, false); // pre-set of GL version, required for extension cache usage + setContextVersion(major, minor, ctxProfileBits, vendorVersion, false); // pre-set of GL version, required for extension cache usage extensionAvailability.reset(this); synchronized(mappedContextTypeObjectLock) { mappedExtensionAvailabilityCache.put(contextFQN, extensionAvailability); @@ -1055,116 +1617,430 @@ public abstract class GLContextImpl extends GLContext { System.err.println(getThreadName() + ": GLContext GL ExtensionAvailabilityCache mapping key("+contextFQN+") -> "+toHexString(extensionAvailability.hashCode()) + " - entries: "+extensionAvailability.getTotalExtensionCount()); } } - } - if( isExtensionAvailable("GL_ARB_ES2_compatibility") ) { + } + + if( isES ) { + if( major >= 3 ) { + ctxProfileBits |= CTX_IMPL_ES3_COMPAT | CTX_IMPL_ES2_COMPAT ; + ctxProfileBits |= CTX_IMPL_FBO; + } else if( major >= 2 ) { + ctxProfileBits |= CTX_IMPL_ES2_COMPAT; + ctxProfileBits |= CTX_IMPL_FBO; + } + } else if( ( major > 4 || major == 4 && minor >= 3 ) || + ( ( major > 3 || major == 3 && minor >= 1 ) && isExtensionAvailable( GLExtensions.ARB_ES3_compatibility ) ) ) { + // See GLContext.isGLES3CompatibleAvailable(..)/isGLES3Compatible() + // Includes [ GL ≥ 4.3, GL ≥ 3.1 w/ GL_ARB_ES3_compatibility and GLES3 ] + ctxProfileBits |= CTX_IMPL_ES3_COMPAT | CTX_IMPL_ES2_COMPAT ; + ctxProfileBits |= CTX_IMPL_FBO; + } else if( isExtensionAvailable( GLExtensions.ARB_ES2_compatibility ) ) { ctxProfileBits |= CTX_IMPL_ES2_COMPAT; + ctxProfileBits |= CTX_IMPL_FBO; + } else if( hasFBOImpl(major, ctxProfileBits, extensionAvailability) ) { + ctxProfileBits |= CTX_IMPL_FBO; } - + + if( ( isES && major == 1 ) || isExtensionAvailable(GLExtensions.OES_single_precision) ) { + ctxProfileBits |= CTX_IMPL_FP32_COMPAT_API; + } + + if(FORCE_NO_FBO_SUPPORT) { + ctxProfileBits &= ~CTX_IMPL_FBO ; + } + // // Set GL Version (complete w/ version string) // - setContextVersion(major, minor, ctxProfileBits, true); - + setContextVersion(major, minor, ctxProfileBits, vendorVersion, true); + + finalizeInit(gl); + setDefaultSwapInterval(); + + final int glErrX = gl.glGetError(); // clear GL error, maybe caused by above operations + + if(DEBUG) { + System.err.println(getThreadName() + ": GLContext.setGLFuncAvail.X: OK "+contextFQN+" - "+GLContext.getGLVersion(ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions, null)+" - glErr "+toHexString(glErrX)); + } + return true; } - protected final void removeCachedVersion(int major, int minor, int ctxProfileBits) { + private final void setRendererQuirks(final AbstractGraphicsDevice adevice, final GLDrawableFactoryImpl factory, + int reqMajor, int reqMinor, int reqCTP, + int major, int minor, int ctp, final VersionNumberString vendorVersion, + boolean withinGLVersionsMapping) { + int[] quirks = new int[GLRendererQuirks.COUNT + 1]; // + 1 ( NoFullFBOSupport ) + int i = 0; + + final String MesaSP = "Mesa "; + // final String MesaRendererAMDsp = " AMD "; + final String MesaRendererIntelsp = "Intel(R)"; + final boolean hwAccel = 0 == ( ctp & GLContext.CTX_IMPL_ACCEL_SOFT ); + final boolean compatCtx = 0 != ( ctp & GLContext.CTX_PROFILE_COMPAT ); + final boolean esCtx = 0 != ( ctp & GLContext.CTX_PROFILE_ES ); + final boolean isX11 = NativeWindowFactory.TYPE_X11 == NativeWindowFactory.getNativeWindowType(true); + final boolean isWindows = Platform.getOSType() == Platform.OSType.WINDOWS; + final boolean isDriverMesa = glRenderer.contains(MesaSP) || glRenderer.contains("Gallium "); + final boolean isDriverATICatalyst = !isDriverMesa && ( glVendor.contains("ATI Technologies") || glRenderer.startsWith("ATI ") ); + final boolean isDriverNVIDIAGeForce = !isDriverMesa && ( glVendor.contains("NVIDIA Corporation") || glRenderer.contains("NVIDIA ") ); + + // + // General Quirks + // + if( esCtx ) { + if( 2 == reqMajor && 2 < major ) { + final int quirk = GLRendererQuirks.GLES3ViaEGLES2Config; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: ES req "+reqMajor+" and 2 < "+major); + } + quirks[i++] = quirk; + if( withinGLVersionsMapping ) { + // Thread safe due to single threaded initialization! + GLRendererQuirks.addStickyDeviceQuirks(adevice, quirks, i-1, 1); + } else { + // FIXME: Remove when moving EGL/ES to ARB ctx creation + synchronized(GLContextImpl.class) { + GLRendererQuirks.addStickyDeviceQuirks(adevice, quirks, i-1, 1); + } + } + } + } + + // + // OS related quirks + // + if( Platform.getOSType() == Platform.OSType.MACOS ) { + // + // OSX + // + { + final int quirk = GLRendererQuirks.NoOffscreenBitmap; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()); + } + quirks[i++] = quirk; + } + if( Platform.getOSVersionNumber().compareTo(Platform.OSXVersion.Mavericks) >= 0 && 3==reqMajor && 4==major ) { + final int quirk = GLRendererQuirks.GL4NeedsGL3Request; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()+", OS Version "+Platform.getOSVersionNumber()+", req "+reqMajor+"."+reqMinor); + } + quirks[i++] = quirk; + if( withinGLVersionsMapping ) { + // Thread safe due to single threaded initialization! + GLRendererQuirks.addStickyDeviceQuirks(adevice, quirks, i-1, 1); + } + } + if( isDriverNVIDIAGeForce ) { + final VersionNumber osxVersionNVFlushClean = new VersionNumber(10,7,3); // < OSX 10.7.3 w/ NV needs glFlush + if( Platform.getOSVersionNumber().compareTo(osxVersionNVFlushClean) < 0 ) { + final int quirk = GLRendererQuirks.GLFlushBeforeRelease; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()+", OS Version "+Platform.getOSVersionNumber()+", Renderer "+glRenderer); + } + quirks[i++] = quirk; + } + if( Platform.getOSVersionNumber().compareTo(Platform.OSXVersion.Lion) < 0 ) { // < OSX 10.7.0 w/ NV has unstable GLSL + final int quirk = GLRendererQuirks.GLSLNonCompliant; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()+", OS Version "+Platform.getOSVersionNumber()+", Renderer "+glRenderer); + } + quirks[i++] = quirk; + } + } + } else if( isWindows ) { + // + // WINDOWS + // + { + final int quirk = GLRendererQuirks.NoDoubleBufferedBitmap; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()); + } + quirks[i++] = quirk; + } + + if( isDriverATICatalyst ) { + final VersionNumber winXPVersionNumber = new VersionNumber ( 5, 1, 0); + final VersionNumber amdSafeMobilityVersion = new VersionNumber(12, 102, 3); + + if ( vendorVersion.compareTo(amdSafeMobilityVersion) < 0 ) { // includes: vendorVersion.isZero() + final int quirk = GLRendererQuirks.NeedCurrCtx4ARBCreateContext; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()+", [Vendor "+glVendor+" or Renderer "+glRenderer+"], driverVersion "+vendorVersion); + } + quirks[i++] = quirk; + } + + if( Platform.getOSVersionNumber().compareTo(winXPVersionNumber) <= 0 ) { + final int quirk = GLRendererQuirks.NeedCurrCtx4ARBPixFmtQueries; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS-Version "+Platform.getOSType()+" "+Platform.getOSVersionNumber()+", [Vendor "+glVendor+" or Renderer "+glRenderer+"]"); + } + quirks[i++] = quirk; + } + } + } else if( Platform.OSType.ANDROID == Platform.getOSType() ) { + // + // ANDROID + // + // Renderer related quirks, may also involve OS + if( glRenderer.contains("PowerVR") ) { + final int quirk = GLRendererQuirks.NoSetSwapInterval; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType() + ", Renderer " + glRenderer); + } + quirks[i++] = quirk; + } + if( glRenderer.contains("Immersion.16") ) { + final int quirk = GLRendererQuirks.GLSharedContextBuggy; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType() + ", Renderer " + glRenderer); + } + quirks[i++] = quirk; + } + } + + // + // Windowing Toolkit related quirks + // + if( isX11 ) { + // + // X11 + // + { + // + // Quirk: DontCloseX11Display + // + final int quirk = GLRendererQuirks.DontCloseX11Display; + if( glRenderer.contains(MesaSP) ) { + if ( glRenderer.contains("X11") && vendorVersion.compareTo(Version800) < 0 ) { + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: X11 Renderer=" + glRenderer + ", Version=[vendor " + vendorVersion + ", GL " + glVersion+"]"); + } + quirks[i++] = quirk; + } + } else if( isDriverATICatalyst ) { + { + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: X11 Renderer=" + glRenderer); + } + quirks[i++] = quirk; + } + } else if( jogamp.nativewindow.x11.X11Util.getMarkAllDisplaysUnclosable() ) { + { + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: X11Util Downstream"); + } + quirks[i++] = quirk; + } + } + } + } + + + // + // RENDERER related quirks + // + if( isDriverMesa ) { + final VersionNumber mesaIntelBuggySharedCtx921 = new VersionNumber(9, 2, 1); + + { + final int quirk = GLRendererQuirks.NoSetSwapIntervalPostRetarget; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: Renderer " + glRenderer); + } + quirks[i++] = quirk; + } + if( hwAccel /* glRenderer.contains( MesaRendererIntelsp ) || glRenderer.contains( MesaRendererAMDsp ) */ ) + { + final int quirk = GLRendererQuirks.NoDoubleBufferedPBuffer; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: Renderer " + glRenderer); + } + quirks[i++] = quirk; + } + if (compatCtx && (major > 3 || (major == 3 && minor >= 1))) { + // FIXME: Apply vendor version constraints! + final int quirk = GLRendererQuirks.GLNonCompliant; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: Renderer " + glRenderer); + } + quirks[i++] = quirk; + } + if( glRenderer.contains( MesaRendererIntelsp ) && + vendorVersion.compareTo(mesaIntelBuggySharedCtx921) >= 0 && isX11 ) { + final int quirk = GLRendererQuirks.GLSharedContextBuggy; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: X11 / Renderer " + glRenderer + " / Mesa-Version "+vendorVersion); + } + quirks[i++] = quirk; + } + if( isWindows && glRenderer.contains("SVGA3D") ) { + final VersionNumber mesaSafeFBOVersion = new VersionNumber(8, 0, 0); + if ( vendorVersion.compareTo(mesaSafeFBOVersion) < 0 ) { // includes: vendorVersion.isZero() + final int quirk = GLRendererQuirks.NoFullFBOSupport; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType() + " / Renderer " + glRenderer + " / Mesa-Version "+vendorVersion); + } + quirks[i++] = quirk; + } + } + } + + // + // Property related quirks + // + if( FORCE_MIN_FBO_SUPPORT ) { + final int quirk = GLRendererQuirks.NoFullFBOSupport; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: property"); + } + quirks[i++] = quirk; + } + + glRendererQuirks = new GLRendererQuirks(quirks, 0, i); + if(DEBUG) { + System.err.println("Quirks local.0: "+glRendererQuirks); + } + { + // Merge sticky quirks, thread safe due to single threaded initialization! + GLRendererQuirks.pushStickyDeviceQuirks(adevice, glRendererQuirks); + + final AbstractGraphicsDevice factoryDefaultDevice = factory.getDefaultDevice(); + if( !GLRendererQuirks.areSameStickyDevice(factoryDefaultDevice, adevice) ) { + GLRendererQuirks.pushStickyDeviceQuirks(factoryDefaultDevice, glRendererQuirks); + } + if( esCtx ) { + final AbstractGraphicsDevice eglFactoryDefaultDevice = GLDrawableFactory.getEGLFactory().getDefaultDevice(); + if( !GLRendererQuirks.areSameStickyDevice(eglFactoryDefaultDevice, adevice) && + !GLRendererQuirks.areSameStickyDevice(eglFactoryDefaultDevice, factoryDefaultDevice) ) { + GLRendererQuirks.pushStickyDeviceQuirks(eglFactoryDefaultDevice, glRendererQuirks); + } + } + } + if(DEBUG) { + System.err.println("Quirks local.X: "+glRendererQuirks); + System.err.println("Quirks sticky on "+adevice+": "+GLRendererQuirks.getStickyDeviceQuirks(adevice)); + } + } + + private static final boolean hasFBOImpl(int major, int ctp, ExtensionAvailabilityCache extCache) { + return ( 0 != (ctp & CTX_PROFILE_ES) && major >= 2 ) || // ES >= 2.0 + + major >= 3 || // any >= 3.0 GL ctx (core, compat and ES) + + ( null != extCache && + + extCache.isExtensionAvailable(GLExtensions.ARB_ES2_compatibility) || // ES 2.0 compatible + + extCache.isExtensionAvailable(GLExtensions.ARB_framebuffer_object) || // ARB_framebuffer_object + + extCache.isExtensionAvailable(GLExtensions.EXT_framebuffer_object) || // EXT_framebuffer_object + + extCache.isExtensionAvailable(GLExtensions.OES_framebuffer_object) ) ; // OES_framebuffer_object excluded + } + + private final void removeCachedVersion(int major, int minor, int ctxProfileBits) { if(!isCurrentContextHardwareRasterizer()) { ctxProfileBits |= GLContext.CTX_IMPL_ACCEL_SOFT; } final AbstractGraphicsConfiguration aconfig = drawable.getNativeSurface().getGraphicsConfiguration(); final AbstractGraphicsDevice adevice = aconfig.getScreen().getDevice(); - + contextFQN = getContextFQN(adevice, major, minor, ctxProfileBits); if (DEBUG) { System.err.println(getThreadName() + ": RM Context FQN: "+contextFQN+" - "+GLContext.getGLVersion(major, minor, ctxProfileBits, null)); } synchronized(mappedContextTypeObjectLock) { - ProcAddressTable table = mappedGLProcAddress.remove( contextFQN ); + final ProcAddressTable table = mappedGLProcAddress.remove( contextFQN ); if(DEBUG) { - System.err.println(getThreadName() + ": RM GLContext GL ProcAddressTable mapping key("+contextFQN+") -> "+table.hashCode()); + final int hc = null != table ? table.hashCode() : 0; + System.err.println(getThreadName() + ": RM GLContext GL ProcAddressTable mapping key("+contextFQN+") -> "+toHexString(hc)); } } synchronized(mappedContextTypeObjectLock) { - ExtensionAvailabilityCache eCache = mappedExtensionAvailabilityCache.remove( contextFQN ); + final ExtensionAvailabilityCache eCache = mappedExtensionAvailabilityCache.remove( contextFQN ); if(DEBUG) { - System.err.println(getThreadName() + ": RM GLContext GL ExtensionAvailabilityCache mapping key("+contextFQN+") -> "+eCache.hashCode()); + final int hc = null != eCache ? eCache.hashCode() : 0; + System.err.println(getThreadName() + ": RM GLContext GL ExtensionAvailabilityCache mapping key("+contextFQN+") -> "+toHexString(hc)); } } } private final boolean isCurrentContextHardwareRasterizer() { boolean isHardwareRasterizer = true; - + if(!drawable.getChosenGLCapabilities().getHardwareAccelerated()) { isHardwareRasterizer = false; } else { - isHardwareRasterizer = ! ( glRendererLowerCase.contains("software") /* Mesa3D */ || - glRendererLowerCase.contains("mesa x11") /* Mesa3D*/ || + isHardwareRasterizer = ! ( glRendererLowerCase.contains("software") /* Mesa3D, Apple */ || + glRendererLowerCase.contains("mesa x11") /* Mesa3D */ || glRendererLowerCase.contains("softpipe") /* Gallium */ || - glRendererLowerCase.contains("llvmpipe") /* Gallium */ + glRendererLowerCase.contains("llvmpipe") /* Gallium */ ); } return isHardwareRasterizer; } - + /** * Updates the platform's 'GLX' function cache */ protected abstract void updateGLXProcAddressTable(); protected abstract StringBuilder getPlatformExtensionsStringImpl(); - + + @Override public final boolean isFunctionAvailable(String glFunctionName) { // Check GL 1st (cached) if(null!=glProcAddressTable) { // null if this context wasn't not created try { - if(0!=glProcAddressTable.getAddressFor(glFunctionName)) { + if( glProcAddressTable.isFunctionAvailable( glFunctionName ) ) { return true; } } catch (Exception e) {} } // Check platform extensions 2nd (cached) - context had to be enabled once - final ProcAddressTable pTable = getPlatformExtProcAddressTable(); - if(null!=pTable) { + final ProcAddressTable pTable = getPlatformExtProcAddressTable(); + if(null!=pTable) { try { - if(0!=pTable.getAddressFor(glFunctionName)) { + if( pTable.isFunctionAvailable( glFunctionName ) ) { return true; } } catch (Exception e) {} } // dynamic function lookup at last incl name aliasing (not cached) - DynamicLookupHelper dynLookup = getDrawableImpl().getGLDynamicLookupHelper(); - String tmpBase = GLExtensionNames.normalizeVEN(GLExtensionNames.normalizeARB(glFunctionName, true), true); - long addr = 0; - int variants = GLExtensionNames.getFuncNamePermutationNumber(tmpBase); - for(int i = 0; 0==addr && i < variants; i++) { - String tmp = GLExtensionNames.getFuncNamePermutation(tmpBase, i); + final DynamicLookupHelper dynLookup = getDrawableImpl().getGLDynamicLookupHelper(); + final String tmpBase = GLNameResolver.normalizeVEN(GLNameResolver.normalizeARB(glFunctionName, true), true); + boolean res = false; + int variants = GLNameResolver.getFuncNamePermutationNumber(tmpBase); + for(int i = 0; !res && i < variants; i++) { + final String tmp = GLNameResolver.getFuncNamePermutation(tmpBase, i); try { - addr = dynLookup.dynamicLookupFunction(tmp); + res = dynLookup.isFunctionAvailable(tmp); } catch (Exception e) { } } - if(0!=addr) { - return true; - } - return false; + return res; } - public boolean isExtensionAvailable(String glExtensionName) { + @Override + public final boolean isExtensionAvailable(String glExtensionName) { if(null!=extensionAvailability) { return extensionAvailability.isExtensionAvailable(mapToRealGLExtensionName(glExtensionName)); } return false; } + @Override public final int getPlatformExtensionCount() { return null != extensionAvailability ? extensionAvailability.getPlatformExtensionCount() : 0; } - + + @Override public final String getPlatformExtensionsString() { if(null!=extensionAvailability) { return extensionAvailability.getPlatformExtensionsString(); @@ -1172,10 +2048,12 @@ public abstract class GLContextImpl extends GLContext { return null; } + @Override public final int getGLExtensionCount() { return null != extensionAvailability ? extensionAvailability.getGLExtensionCount() : 0; } - + + @Override public final String getGLExtensionsString() { if(null!=extensionAvailability) { return extensionAvailability.getGLExtensionsString(); @@ -1189,15 +2067,15 @@ public abstract class GLContextImpl extends GLContext { } return false; } - + protected static String getContextFQN(AbstractGraphicsDevice device, int major, int minor, int ctxProfileBits) { // remove non-key values - ctxProfileBits &= ~( GLContext.CTX_OPTION_DEBUG | GLContext.CTX_IMPL_ES2_COMPAT ) ; - + ctxProfileBits &= CTX_IMPL_CACHE_MASK; + return device.getUniqueID() + "-" + toHexString(composeBits(major, minor, ctxProfileBits)); } - protected String getContextFQN() { + protected final String getContextFQN() { return contextFQN; } @@ -1208,32 +2086,63 @@ public abstract class GLContextImpl extends GLContext { throw new GLException("Not supported on non-pbuffer contexts"); } - /** On some platforms the mismatch between OpenGL's coordinate - system (origin at bottom left) and the window system's - coordinate system (origin at top left) necessitates a vertical - flip of pixels read from offscreen contexts. */ - public abstract boolean offscreenImageNeedsVerticalFlip(); + @Override + public int getDefaultPixelDataType() { + evalPixelDataType(); + return pixelDataType; + } - /** Only called for offscreen contexts; needed by glReadPixels */ - public abstract int getOffscreenContextPixelDataType(); + @Override + public int getDefaultPixelDataFormat() { + evalPixelDataType(); + return pixelDataFormat; + } + private final void evalPixelDataType() { + if(!pixelDataEvaluated) { + synchronized(this) { + if(!pixelDataEvaluated) { + boolean ok = false; + /* if(isGL2GL3() && 3 == components) { + pixelDataInternalFormat=GL.GL_RGB; + pixelDataFormat=GL.GL_RGB; + pixelDataType = GL.GL_UNSIGNED_BYTE; + ok = true; + } else */ if( isGLES2Compatible() || isExtensionAvailable(GLExtensions.OES_read_format) ) { + final int[] glImplColorReadVals = new int[] { 0, 0 }; + gl.glGetIntegerv(GL.GL_IMPLEMENTATION_COLOR_READ_FORMAT, glImplColorReadVals, 0); + gl.glGetIntegerv(GL.GL_IMPLEMENTATION_COLOR_READ_TYPE, glImplColorReadVals, 1); + // pixelDataInternalFormat = (4 == components) ? GL.GL_RGBA : GL.GL_RGB; + pixelDataFormat = glImplColorReadVals[0]; + pixelDataType = glImplColorReadVals[1]; + ok = 0 != pixelDataFormat && 0 != pixelDataType; + } + if( !ok ) { + // RGBA read is safe for all GL profiles + // pixelDataInternalFormat = (4 == components) ? GL.GL_RGBA : GL.GL_RGB; + pixelDataFormat=GL.GL_RGBA; + pixelDataType = GL.GL_UNSIGNED_BYTE; + } + // TODO: Consider: + // return gl.isGL2GL3()?GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV:GL.GL_UNSIGNED_SHORT_5_5_5_1; + pixelDataEvaluated = true; + } + } + } + } //---------------------------------------------------------------------- // Helpers for buffer object optimizations - - public void setBufferSizeTracker(GLBufferSizeTracker bufferSizeTracker) { - this.bufferSizeTracker = bufferSizeTracker; - } - public GLBufferSizeTracker getBufferSizeTracker() { - return bufferSizeTracker; + public final GLBufferObjectTracker getBufferObjectTracker() { + return bufferObjectTracker; } - public GLBufferStateTracker getBufferStateTracker() { + public final GLBufferStateTracker getBufferStateTracker() { return bufferStateTracker; } - public GLStateTracker getGLStateTracker() { + public final GLStateTracker getGLStateTracker() { return glStateTracker; } @@ -1242,38 +2151,123 @@ public abstract class GLContextImpl extends GLContext { // current on the OpenGL worker thread // - public boolean hasWaiters() { + /** + * Returns true if the given thread is owner, otherwise false. + * <p> + * Method exists merely for code validation of {@link #isCurrent()}. + * </p> + */ + public final boolean isOwner(Thread thread) { + return lock.isOwner(thread); + } + + /** + * Returns true if there are other threads waiting for this GLContext to {@link #makeCurrent()}, otherwise false. + * <p> + * Since method does not perform any synchronization, accurate result are returned if lock is hold - only. + * </p> + */ + public final boolean hasWaiters() { return lock.getQueueLength()>0; } - + + /** + * Returns the number of hold locks. See {@link RecursiveLock#getHoldCount()} for semantics. + * <p> + * Since method does not perform any synchronization, accurate result are returned if lock is hold - only. + * </p> + */ + public final int getLockCount() { + return lock.getHoldCount(); + } + + //--------------------------------------------------------------------------- + // Special FBO hook + // + + /** + * Tracks {@link GL#GL_FRAMEBUFFER}, {@link GL2GL3#GL_DRAW_FRAMEBUFFER} and {@link GL2GL3#GL_READ_FRAMEBUFFER} + * to be returned via {@link #getBoundFramebuffer(int)}. + * + * <p>Invoked by {@link GL#glBindFramebuffer(int, int)}. </p> + * + * <p>Assumes valid <code>framebufferName</code> range of [0..{@link Integer#MAX_VALUE}]</p> + * + * <p>Does not throw an exception if <code>target</code> is unknown or <code>framebufferName</code> invalid.</p> + */ + public final void setBoundFramebuffer(int target, int framebufferName) { + if(0 > framebufferName) { + return; // ignore invalid name + } + switch(target) { + case GL.GL_FRAMEBUFFER: + boundFBOTarget[0] = framebufferName; // draw + boundFBOTarget[1] = framebufferName; // read + break; + case GL2GL3.GL_DRAW_FRAMEBUFFER: + boundFBOTarget[0] = framebufferName; // draw + break; + case GL2GL3.GL_READ_FRAMEBUFFER: + boundFBOTarget[1] = framebufferName; // read + break; + default: // ignore untracked target + } + } + @Override + public final int getBoundFramebuffer(int target) { + switch(target) { + case GL.GL_FRAMEBUFFER: + case GL2GL3.GL_DRAW_FRAMEBUFFER: + return boundFBOTarget[0]; // draw + case GL2GL3.GL_READ_FRAMEBUFFER: + return boundFBOTarget[1]; // read + default: + throw new InternalError("Invalid FBO target name: "+toHexString(target)); + } + } + + @Override + public final int getDefaultDrawFramebuffer() { return drawable.getDefaultDrawFramebuffer(); } + @Override + public final int getDefaultReadFramebuffer() { return drawable.getDefaultReadFramebuffer(); } + @Override + public final int getDefaultReadBuffer() { return drawable.getDefaultReadBuffer(gl, drawableRead != drawable); } + //--------------------------------------------------------------------------- // GL_ARB_debug_output, GL_AMD_debug_output helpers // + @Override public final String getGLDebugMessageExtension() { return glDebugHandler.getExtension(); } + @Override public final boolean isGLDebugMessageEnabled() { return glDebugHandler.isEnabled(); } - + + @Override public final int getContextCreationFlags() { - return additionalCtxCreationFlags; + return additionalCtxCreationFlags; } + @Override public final void setContextCreationFlags(int flags) { if(!isCreated()) { additionalCtxCreationFlags = flags & GLContext.CTX_OPTION_DEBUG; } } - - public final boolean isGLDebugSynchronous() { return glDebugHandler.isSynchronous(); } - + + @Override + public final boolean isGLDebugSynchronous() { return glDebugHandler.isSynchronous(); } + + @Override public final void setGLDebugSynchronous(boolean synchronous) { glDebugHandler.setSynchronous(synchronous); } - + + @Override public final void enableGLDebugMessage(boolean enable) throws GLException { if(!isCreated()) { if(enable) { @@ -1286,40 +2280,48 @@ public abstract class GLContextImpl extends GLContext { glDebugHandler.enable(enable); } } - - public final void addGLDebugListener(GLDebugListener listener) { + + @Override + public final void addGLDebugListener(GLDebugListener listener) { glDebugHandler.addListener(listener); } - + + @Override public final void removeGLDebugListener(GLDebugListener listener) { glDebugHandler.removeListener(listener); - } - + } + + @Override public final void glDebugMessageControl(int source, int type, int severity, int count, IntBuffer ids, boolean enabled) { if(glDebugHandler.isExtensionARB()) { - gl.getGL2GL3().glDebugMessageControlARB(source, type, severity, count, ids, enabled); + gl.getGL2GL3().glDebugMessageControl(source, type, severity, count, ids, enabled); } else if(glDebugHandler.isExtensionAMD()) { gl.getGL2GL3().glDebugMessageEnableAMD(GLDebugMessage.translateARB2AMDCategory(source, type), severity, count, ids, enabled); - } + } } - + + @Override public final void glDebugMessageControl(int source, int type, int severity, int count, int[] ids, int ids_offset, boolean enabled) { if(glDebugHandler.isExtensionARB()) { - gl.getGL2GL3().glDebugMessageControlARB(source, type, severity, count, ids, ids_offset, enabled); + gl.getGL2GL3().glDebugMessageControl(source, type, severity, count, ids, ids_offset, enabled); } else if(glDebugHandler.isExtensionAMD()) { gl.getGL2GL3().glDebugMessageEnableAMD(GLDebugMessage.translateARB2AMDCategory(source, type), severity, count, ids, ids_offset, enabled); } } - + + @Override public final void glDebugMessageInsert(int source, int type, int id, int severity, String buf) { final int len = (null != buf) ? buf.length() : 0; if(glDebugHandler.isExtensionARB()) { - gl.getGL2GL3().glDebugMessageInsertARB(source, type, id, severity, len, buf); + gl.getGL2GL3().glDebugMessageInsert(source, type, id, severity, len, buf); } else if(glDebugHandler.isExtensionAMD()) { gl.getGL2GL3().glDebugMessageInsertAMD(GLDebugMessage.translateARB2AMDCategory(source, type), severity, id, len, buf); - } + } } - + /** Internal bootstraping glGetString(GL_RENDERER) */ - protected static native String glGetStringInt(int name, long procAddress); + private static native String glGetStringInt(int name, long procAddress); + + /** Internal bootstraping glGetIntegerv(..) for version */ + private static native void glGetIntegervInt(int pname, int[] params, int params_offset, long procAddress); } |