/*
* 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
* PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
* MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
* ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
* DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
* 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.IntBuffer;
import java.security.PrivilegedAction;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import com.jogamp.common.ExceptionUtils;
import com.jogamp.common.os.DynamicLookupHelper;
import com.jogamp.common.os.Platform;
import com.jogamp.common.util.Bitfield;
import com.jogamp.common.util.ReflectionUtil;
import com.jogamp.common.util.SecurityUtil;
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.GLNameResolver;
import com.jogamp.gluegen.runtime.opengl.GLProcAddressResolver;
import com.jogamp.opengl.GLExtensions;
import com.jogamp.opengl.GLRendererQuirks;
import com.jogamp.nativewindow.AbstractGraphicsConfiguration;
import com.jogamp.nativewindow.AbstractGraphicsDevice;
import com.jogamp.nativewindow.NativeSurface;
import com.jogamp.nativewindow.NativeWindowFactory;
import com.jogamp.nativewindow.ProxySurface;
import com.jogamp.nativewindow.egl.EGLGraphicsDevice;
import com.jogamp.opengl.GL;
import com.jogamp.opengl.GL2ES2;
import com.jogamp.opengl.GL2ES3;
import com.jogamp.opengl.GL2GL3;
import com.jogamp.opengl.GL3;
import com.jogamp.opengl.GLCapabilitiesImmutable;
import com.jogamp.opengl.GLContext;
import com.jogamp.opengl.GLDebugListener;
import com.jogamp.opengl.GLDebugMessage;
import com.jogamp.opengl.GLDrawable;
import com.jogamp.opengl.GLDrawableFactory;
import com.jogamp.opengl.GLException;
import com.jogamp.opengl.GLPipelineFactory;
import com.jogamp.opengl.GLProfile;
public abstract class GLContextImpl extends GLContext {
/**
* Context full qualified name: display_type + display_connection + major + minor + ctp.
* This is the key for all cached GL ProcAddressTables, etc, to support multi display/device setups.
*/
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;
// Table that holds the addresses of the native C-language entry points for
// OpenGL functions.
private ProcAddressTable glProcAddressTable;
private String glVendor;
private String glRenderer;
private String glRendererLowerCase;
private String glVersion;
private boolean glGetPtrInit = false;
private long glGetStringPtr = 0;
private long glGetIntegervPtr = 0;
private long glGetStringiPtr = 0;
// Tracks lifecycle of buffer objects to avoid
// repeated glGet calls upon glMapBuffer operations
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;
/**
*
*
[GLAutoDrawable.upstreamLock].lock()
*
drawable.surface.lock()
*
contextLock.lock()
*
*/
protected GLDrawableImpl drawable;
protected GLDrawableImpl drawableRead;
/**
* If GL >= 3.0 (ES or desktop) and not having {@link GLRendererQuirks#NoSurfacelessCtx},
* being evaluated if not surface-handle is null and not yet set at makeCurrent(..).
*/
private boolean isSurfaceless = false;
private boolean pixelDataEvaluated;
private int /* pixelDataInternalFormat, */ pixelDataFormat, pixelDataType;
private int currentSwapInterval;
protected GL gl;
protected static final Object mappedContextTypeObjectLock;
protected static final HashMap mappedExtensionAvailabilityCache;
protected static final HashMap mappedGLProcAddress;
protected static final HashMap mappedGLXProcAddress;
static {
mappedContextTypeObjectLock = new Object();
mappedExtensionAvailabilityCache = new HashMap();
mappedGLProcAddress = new HashMap();
mappedGLXProcAddress = new HashMap();
}
public static void shutdownImpl() {
mappedExtensionAvailabilityCache.clear();
mappedGLProcAddress.clear();
mappedGLXProcAddress.clear();
}
public GLContextImpl(final GLDrawableImpl drawable, final GLContext shareWith) {
super();
if( null == drawable ) {
throw new IllegalArgumentException("Null drawable");
}
bufferStateTracker = new GLBufferStateTracker();
if ( null != shareWith ) {
GLContextShareSet.registerSharing(this, shareWith);
bufferObjectTracker = ((GLContextImpl)shareWith).getBufferObjectTracker();
if( null == bufferObjectTracker ) {
throw new InternalError("shared-master context hash null GLBufferObjectTracker: "+toHexString(shareWith.hashCode()));
}
} else {
bufferObjectTracker = new GLBufferObjectTracker();
}
this.drawable = drawable;
this.drawableRead = drawable;
this.glDebugHandler = new GLDebugMessageHandler(this);
}
private final void clearStates() {
if( !GLContextShareSet.hasCreatedSharesLeft(this) ) {
bufferObjectTracker.clear();
}
bufferStateTracker.clear();
glStateTracker.setEnabled(false);
glStateTracker.clearStates();
}
@Override
protected void resetStates(final boolean isInit) {
if( !isInit ) {
clearStates();
}
extensionAvailability = null;
glProcAddressTable = null;
gl = null;
contextFQN = null;
additionalCtxCreationFlags = 0;
glVendor = "";
glRenderer = glVendor;
glRendererLowerCase = glRenderer;
glVersion = glVendor;
glGetPtrInit = false;
glGetStringPtr = 0;
glGetIntegervPtr = 0;
glGetStringiPtr = 0;
if ( !isInit && null != boundFBOTarget ) { // : boundFBOTarget is not written yet
boundFBOTarget[0] = 0; // draw
boundFBOTarget[1] = 0; // read
}
isSurfaceless = false;
pixelDataEvaluated = false;
currentSwapInterval = 0;
super.resetStates(isInit);
}
@Override
public final GLDrawable setGLReadDrawable(final GLDrawable read) {
// Validate constraints first!
if(!isGLReadDrawableAvailable()) {
throw new GLException("Setting read drawable feature not available");
}
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(..)");
}
if(lockHeld) {
release(false);
}
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(final GLDrawable readWrite, final boolean setWriteOnly) {
// Validate constraints first!
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(..)");
}
if( drawable == readWrite && ( setWriteOnly || drawableRead == readWrite ) ) {
return drawable; // no change.
}
final GLDrawableImpl oldDrawableWrite = drawable;
final GLDrawableImpl oldDrawableRead = drawableRead;
if( isCreated() && null != oldDrawableWrite && oldDrawableWrite.isRealized() ) {
if(!lockHeld) {
makeCurrent();
}
// sync GL ctx w/ drawable's framebuffer before de-association
gl.glFinish();
associateDrawable(false);
if(!lockHeld) {
release(false);
}
}
if(lockHeld) {
release(false);
}
if( !setWriteOnly || drawableRead == drawable ) { // if !setWriteOnly || !explicitReadDrawable
drawableRead = (GLDrawableImpl) readWrite;
}
drawableRetargeted |= null != drawable && readWrite != drawable;
drawable = (GLDrawableImpl) readWrite ;
if( isCreated() && null != drawable && drawable.isRealized() ) {
int res = CONTEXT_NOT_CURRENT;
Throwable gle = null;
try {
res = makeCurrent(true); // implicit: associateDrawable(true)
} catch ( final Throwable t ) {
gle = t;
} finally {
if( CONTEXT_NOT_CURRENT == res ) {
// Failure, recover and bail out w/ GLException
drawableRead = oldDrawableRead;
drawable = oldDrawableWrite;
if( drawable.isRealized() ) {
makeCurrent(true); // implicit: associateDrawable(true)
}
if( !lockHeld ) {
release(false);
}
final String msg = "Error: makeCurrent() failed with new drawable "+readWrite;
if( null != gle ) {
throw new GLException(msg, gle);
} else {
throw new GLException(msg);
}
}
}
if( !lockHeld ) {
release(false);
}
}
return oldDrawableWrite;
}
@Override
public final GLDrawable getGLDrawable() {
return drawable;
}
public final GLDrawableImpl getDrawableImpl() {
return drawable;
}
@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(final GL gl) {
if( DEBUG ) {
final String sgl1 = (null!=this.gl)?this.gl.getClass().getSimpleName()+", "+this.gl.toString():"";
final String sgl2 = (null!=gl)?gl.getClass().getSimpleName()+", "+gl.toString():"";
System.err.println("Info: setGL (OpenGL "+getGLVersion()+"): "+getThreadName()+", "+sgl1+" -> "+sgl2);
ExceptionUtils.dumpStack(System.err);
}
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).
*
*
* This is currently being used and overridden by Mac OSX,
* which issues the {@link jogamp.opengl.macosx.cgl.CGL#updateContext(long) NSOpenGLContext update()} call.
*
*
* @throws GLException
*/
protected void drawableUpdatedNotify() throws GLException { }
public abstract Object getPlatformGLExtensions();
// Note: the surface is locked within [makeCurrent .. swap .. release]
@Override
public void release() throws GLException {
release(false);
}
private String getTraceSwitchMsg() {
final long drawH = null != drawable ? drawable.getHandle() : 0;
return "obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", isShared "+GLContextShareSet.isShared(this)+", surf "+(null!=drawable)+" "+toHexString(drawH)+", "+lock;
}
private void release(final boolean inDestruction) throws GLException {
if( TRACE_SWITCH ) {
System.err.println(getThreadName() +": GLContext.ContextSwitch[release.0, inDestruction: "+inDestruction+"]: "+getTraceSwitchMsg());
}
if ( !lock.isOwner(Thread.currentThread()) ) {
final String msg = getThreadName() +": Context not current on thread, inDestruction: "+inDestruction+", "+getTraceSwitchMsg();
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);
}
Throwable drawableContextMadeCurrentException = null;
final boolean actualRelease = ( inDestruction || lock.getHoldCount() == 1 ) && 0 != contextHandle;
try {
if( actualRelease ) {
if( !inDestruction ) {
try {
contextMadeCurrent(false);
} catch (final Throwable t) {
drawableContextMadeCurrentException = t;
}
}
releaseImpl();
}
} finally {
// exception prone ..
if( actualRelease ) {
setCurrent(null);
}
lock.unlock();
drawable.unlockSurface();
if( DEBUG_TRACE_SWITCH ) {
final String msg = getThreadName() +": GLContext.ContextSwitch[release.X]: "+(actualRelease?"switch":"keep ")+" - "+getTraceSwitchMsg();
lastCtxReleaseStack = new Throwable(msg);
if( TRACE_SWITCH ) {
System.err.println(msg);
// ExceptionUtils.dumpStack(System.err, 0, 10);
}
}
}
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: "+getTraceSwitchMsg());
}
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.
lock.lock(); // holdCount++ -> 1 - n (1: not locked, 2-n: destroy while rendering)
if ( DEBUG_TRACE_SWITCH ) {
if ( lock.getHoldCount() > 2 ) {
System.err.println(getThreadName() + ": GLContextImpl.destroy: Lock was hold more than once - makeCurrent/release imbalance: "+getTraceSwitchMsg());
ExceptionUtils.dumpStack(System.err);
}
}
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());
}
}
try {
associateDrawable(false);
} catch (final 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);
}
destroyImpl();
contextHandle = 0;
glDebugHandler = null;
// this maybe impl. in a platform specific way to release remaining shared ctx.
if( GLContextShareSet.contextDestroyed(this) && !GLContextShareSet.hasCreatedSharesLeft(this) ) {
GLContextShareSet.unregisterSharing(this);
}
resetStates(false);
} finally {
lock.unlock();
if ( DEBUG_TRACE_SWITCH ) {
System.err.println(getThreadName() + ": GLContextImpl.destroy.X: "+getTraceSwitchMsg());
}
}
} finally {
drawable.unlockSurface();
}
if( null != associateDrawableException ) {
throw new GLException("Exception @ destroy's associateDrawable(false)", associateDrawableException);
}
} else {
resetStates(false);
}
}
protected abstract void destroyImpl() throws GLException;
@Override
public final void copy(final GLContext source, final int mask) throws GLException {
if (source.getHandle() == 0) {
throw new GLException("Source OpenGL context has not been created");
}
if (getHandle() == 0) {
throw new GLException("Destination OpenGL context has not been created");
}
final int lockRes = drawable.lockSurface();
if (NativeSurface.LOCK_SURFACE_NOT_READY >= lockRes) {
// this would be odd ..
throw new GLException("Surface not ready to lock");
}
try {
copyImpl(source, mask);
} finally {
drawable.unlockSurface();
}
}
protected abstract void copyImpl(GLContext source, int mask) throws GLException;
//----------------------------------------------------------------------
//
protected final boolean isSurfaceless() { return isSurfaceless; }
/**
* {@inheritDoc}
*
* MakeCurrent functionality, which also issues the creation of the actual OpenGL context.
*
* The complete callgraph for general OpenGL context creation is:
*
*
* Once at startup, ie triggered by the singleton constructor of a {@link GLDrawableFactoryImpl} specialization,
* calling {@link #createContextARB} will query all available OpenGL versions:
*
*
FOR ALL GL* DO:
*
*
{@link #createContextARBMapVersionsAvailable}
*
*
{@link #createContextARBVersions}
*
*
{@link #mapVersionAvailable}
*
*
*
* @see #makeCurrentImpl
* @see #create
* @see #createContextARB
* @see #createContextARBImpl
* @see #mapVersionAvailable
* @see #destroyContextARBImpl
*/
@Override
public final int makeCurrent() throws GLException {
return makeCurrent(false);
}
protected final int makeCurrent(boolean forceDrawableAssociation) throws GLException {
final boolean hasDrawable = null != drawable;
if( TRACE_SWITCH ) {
System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.0]: "+getTraceSwitchMsg());
}
if( !hasDrawable ) {
if( DEBUG_TRACE_SWITCH ) {
System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.X0]: NULL Drawable - CONTEXT_NOT_CURRENT - "+getTraceSwitchMsg());
}
return CONTEXT_NOT_CURRENT;
}
// Note: the surface is locked within [makeCurrent .. swap .. release]
final int lockRes = drawable.lockSurface();
if (NativeSurface.LOCK_SURFACE_NOT_READY >= lockRes) {
if( DEBUG_TRACE_SWITCH ) {
System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.X1]: Surface Not Ready - CONTEXT_NOT_CURRENT - "+getTraceSwitchMsg());
}
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 ( drawable.isRealized() ) {
lock.lock();
try {
if ( 0 == drawable.getHandle() && !isSurfaceless ) {
if( DEBUG ) {
System.err.println(getThreadName() +": GLContext.makeCurrent: Surfaceless evaluate");
}
if( hasRendererQuirk(GLRendererQuirks.NoSurfacelessCtx) ) {
throw new GLException(String.format("Surfaceless not supported due to quirk %s: %s",
GLRendererQuirks.toString(GLRendererQuirks.NoSurfacelessCtx), toString()));
}
// Allow probing if ProxySurface && OPT_UPSTREAM_SURFACELESS
final NativeSurface surface = drawable.getNativeSurface();
if( !(surface instanceof ProxySurface) ||
!((ProxySurface)surface).containsUpstreamOptionBits( ProxySurface.OPT_UPSTREAM_SURFACELESS ) ) {
throw new GLException(String.format("non-surfaceless drawable has zero-handle: %s", drawable.toString()));
}
}
// 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]: KEEP - CONTEXT_CURRENT - "+getTraceSwitchMsg());
}
return CONTEXT_CURRENT;
} else {
current.release();
}
}
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 (final 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 (final RuntimeException e) {
unlockResources = true;
throw e;
} finally {
if (unlockResources) {
drawable.unlockSurface();
}
}
if ( CONTEXT_NOT_CURRENT != res ) { // still locked!
if( 0 == drawable.getHandle() && !isSurfaceless ) {
if( hasRendererQuirk(GLRendererQuirks.NoSurfacelessCtx) ) {
throw new GLException(String.format("Surfaceless not supported due to quirk %s: %s",
GLRendererQuirks.toString(GLRendererQuirks.NoSurfacelessCtx), toString()));
}
if( DEBUG ) {
System.err.println(getThreadName() +": GLContext.makeCurrent: Surfaceless OK - validated");
}
isSurfaceless = true;
}
setCurrent(this);
if( CONTEXT_CURRENT_NEW == res ) {
// check if the drawable's and the GL's GLProfile are equal
// throws an GLException if not
// FIXME: drawable.getGLProfile().verifyEquality(gl.getGLProfile());
glDebugHandler.init( isGLDebugEnabled() );
if(DEBUG_GL) {
setGL( GLPipelineFactory.create("com.jogamp.opengl.Debug", null, gl, null) );
if(glDebugHandler.isEnabled()) {
glDebugHandler.addListener(new GLDebugMessageHandler.StdErrGLDebugListener(true));
}
}
if(TRACE_GL) {
setGL( GLPipelineFactory.create("com.jogamp.opengl.Trace", null, gl, new Object[] { System.err } ) );
}
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
// FIXME: not sure what to do here if this throws
if (deletedObjectTracker != null) {
deletedObjectTracker.clean(getGL());
}
*/
}
if( TRACE_SWITCH ) {
System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.X3]: SWITCH - "+makeCurrentResultToString(res)+" - stateTracker.on "+glStateTracker.isEnabled()+" - "+getTraceSwitchMsg());
}
return res;
}
private final GLContextImpl getOtherSharedMaster() {
final GLContextImpl sharedMaster = (GLContextImpl) GLContextShareSet.getSharedMaster(this);
return this != sharedMaster ? sharedMaster : null;
}
private final int makeCurrentWithinLock(final int surfaceLockRes) throws GLException {
if (!isCreated()) {
if( 0 >= drawable.getSurfaceWidth() || 0 >= drawable.getSurfaceHeight() ) {
if ( DEBUG_TRACE_SWITCH ) {
System.err.println(getThreadName() + ": Create GL context REJECTED (zero surface size) for " + getClass().getName()+" - "+getTraceSwitchMsg());
System.err.println(drawable.toString());
}
return CONTEXT_NOT_CURRENT;
}
if(DEBUG_GL) {
// only impacts w/ createContextARB(..)
additionalCtxCreationFlags |= GLContext.CTX_OPTION_DEBUG ;
}
final boolean created;
final GLContextImpl sharedMaster = getOtherSharedMaster();
if ( null != sharedMaster ) {
if ( NativeSurface.LOCK_SURFACE_NOT_READY >= sharedMaster.drawable.lockSurface() ) {
throw new GLException("GLContextShareSet could not lock sharedMaster surface: "+sharedMaster.drawable);
}
}
try {
if ( null != sharedMaster ) {
final long sharedMasterHandle = sharedMaster.getHandle();
if ( 0 == sharedMasterHandle ) {
throw new GLException("GLContextShareSet returned an invalid sharedMaster context: "+sharedMaster);
}
created = createImpl(sharedMasterHandle); // may throws exception if fails
} else {
created = createImpl(0); // may throws exception if fails
}
if( created && hasNoDefaultVAO() ) {
final int[] tmp = new int[1];
final GL rootGL = gl.getRootGL();
final GL2ES3 gl2es3 = rootGL.getGL2ES3();
gl2es3.glGenVertexArrays(1, tmp, 0);
defaultVAO = tmp[0];
gl2es3.glBindVertexArray(defaultVAO);
}
} finally {
if ( null != sharedMaster ) {
sharedMaster.drawable.unlockSurface();
}
}
if ( DEBUG_TRACE_SWITCH ) {
System.err.println(getThreadName() + ": Create GL context "+(created?"OK":"FAILED")+": For " + getClass().getName()+" - "+getGLVersion()+" - "+getTraceSwitchMsg());
// ExceptionUtils.dumpStack(System.err, 0, 10);
}
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();
// 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(Version3_0) <= 0 ) {
reqMajor = 2;
} else {
reqMajor = ctxVersion.getMajor();
}
final boolean isCompat;
if( 0 != ( ctxOptions & GLContext.CTX_PROFILE_CORE) ) {
reqProfile = GLContext.CTX_PROFILE_CORE;
isCompat = false;
} else {
reqProfile = GLContext.CTX_PROFILE_COMPAT;
isCompat = true;
}
final MappedGLVersion me = mapAvailableGLVersion(device, reqMajor, reqProfile, ctxVersion, ctxOptions, glRendererQuirks);
// Perform all required profile mappings
if( isCompat ) {
// COMPAT via non ARB
mapAvailableGLVersion(device, reqMajor, GLContext.CTX_PROFILE_CORE, ctxVersion, ctxOptions, glRendererQuirks);
if( reqMajor >= 4 ) {
mapAvailableGLVersion(device, 3, reqProfile, ctxVersion, ctxOptions, glRendererQuirks);
mapAvailableGLVersion(device, 3, GLContext.CTX_PROFILE_CORE, ctxVersion, ctxOptions, glRendererQuirks);
}
if( reqMajor >= 3 ) {
mapAvailableGLVersion(device, 2, reqProfile, ctxVersion, ctxOptions, glRendererQuirks);
}
} else {
// CORE via non ARB, unlikely, however ..
if( reqMajor >= 4 ) {
mapAvailableGLVersion(device, 3, reqProfile, ctxVersion, ctxOptions, glRendererQuirks);
}
}
GLContext.setAvailableGLVersionsSet(device, true);
if (DEBUG) {
System.err.println(getThreadName() + ": createContextOLD-MapGLVersions HAVE: " + me);
}
}
}
}
GLContextShareSet.contextCreated(this);
return CONTEXT_CURRENT_NEW;
}
makeCurrentImpl();
return CONTEXT_CURRENT;
}
protected abstract void makeCurrentImpl() throws GLException;
/**
* Calls {@link GLDrawableImpl#associateContext(GLContext, boolean)}
*/
protected void associateDrawable(final boolean bound) {
drawable.associateContext(this, bound);
}
/**
* Calls {@link GLDrawableImpl#contextMadeCurrent(GLContext, boolean)}
*/
protected void contextMadeCurrent(final boolean current) {
drawable.contextMadeCurrent(this, current);
}
/**
* Platform dependent entry point for context creation.
*
* This method is called from {@link #makeCurrentWithinLock()} .. {@link #makeCurrent()} .
*
*
* The implementation shall verify this context with a
* MakeContextCurrent call.
*
*
* The implementation must leave the context current.
*
*
* Non fatal context creation failure via return {@code false}
* is currently implemented for: {@code MacOSXCGLContext}.
*
* @param sharedWithHandle the shared context handle or 0
* @return {@code true} if successful. Method returns {@code false} if the context creation failed non fatally,
* hence it may be created at a later time. Otherwise method throws {@link GLException}.
* @throws GLException if method fatally fails creating the context and no attempt shall be made at a later time.
*/
protected abstract boolean createImpl(long sharedWithHandle) throws GLException ;
/**
* Platform dependent but harmonized implementation of the ARB_create_context
* mechanism to create a context.
*
* This method is called from {@link #createContextARB}, {@link #createImpl(long)} .. {@link #makeCurrent()} .
*
* The implementation shall verify this context with a
* MakeContextCurrent call.
*
* The implementation must leave the context current.
*
* @param share the shared context or null
* @param direct flag if direct is requested
* @param ctxOptionFlags ARB_create_context related, see references below
* @param major major number
* @param minor minor number
* @return the valid and current context if successful, or null
*
* @see #makeCurrent
* @see #CTX_PROFILE_COMPAT
* @see #CTX_OPTION_FORWARD
* @see #CTX_OPTION_DEBUG
* @see #makeCurrentImpl
* @see #create
* @see #createContextARB
* @see #createContextARBImpl
* @see #destroyContextARBImpl
*/
protected abstract long createContextARBImpl(long share, boolean direct, int ctxOptionFlags, int major, int minor);
/**
* Destroy the context created by {@link #createContextARBImpl}.
*
* @see #makeCurrent
* @see #makeCurrentImpl
* @see #create
* @see #createContextARB
* @see #createContextARBImpl
* @see #destroyContextARBImpl
*/
protected abstract void destroyContextARBImpl(long context);
protected final boolean isCreateContextARBAvail(final AbstractGraphicsDevice device) {
return !GLProfile.disableOpenGLARBContext &&
!GLRendererQuirks.existStickyDeviceQuirk(device, GLRendererQuirks.NoARBCreateContext);
}
protected final String getCreateContextARBAvailStr(final AbstractGraphicsDevice device) {
final boolean noARBCreateContext = GLRendererQuirks.existStickyDeviceQuirk(device, GLRendererQuirks.NoARBCreateContext);
return "disabled "+GLProfile.disableOpenGLARBContext+", quirk "+noARBCreateContext;
}
/**
* Platform independent part of using the ARB_create_context
* mechanism to create a context.
*
* The implementation of {@link #create} shall use this protocol in case the platform supports ARB_create_context.
*
* This method may call {@link #createContextARBImpl} and {@link #destroyContextARBImpl}.
*
* This method will also query all available native OpenGL context when first called,
* usually the first call should happen with the shared GLContext of the DrawableFactory.
*
* The implementation makes the context current, if successful
*
* @see #makeCurrentImpl
* @see #create
* @see #createContextARB
* @see #createContextARBImpl
* @see #destroyContextARBImpl
*/
protected final long createContextARB(final long share, final boolean direct)
{
final AbstractGraphicsConfiguration config = drawable.getNativeSurface().getGraphicsConfiguration();
final AbstractGraphicsDevice device = config.getScreen().getDevice();
final GLCapabilitiesImmutable glCaps = (GLCapabilitiesImmutable) config.getChosenCapabilities();
final GLProfile glp = glCaps.getGLProfile();
if (DEBUG) {
System.err.println(getThreadName() + ": createContextARB-MapGLVersions is SET ("+device.getConnection()+"): "+
GLContext.getAvailableGLVersionsSet(device));
}
if ( !GLContext.getAvailableGLVersionsSet(device) ) {
if( !mapGLVersions(device) ) {
// none of the ARB context creation calls was successful, bail out
return 0;
}
}
final int[] reqMajorCTP = new int[] { 0, 0 };
GLContext.getRequestMajorAndCompat(glp, reqMajorCTP);
if(DEBUG) {
System.err.println(getThreadName() + ": createContextARB-MapGLVersions requested "+glp+" -> "+GLContext.getGLVersion(reqMajorCTP[0], 0, reqMajorCTP[1], null));
}
final int _major[] = { 0 };
final int _minor[] = { 0 };
final int _ctp[] = { 0 };
long _ctx = 0;
if( GLContext.getAvailableGLVersion(device, reqMajorCTP[0], reqMajorCTP[1],
_major, _minor, _ctp)) {
_ctp[0] |= additionalCtxCreationFlags;
if(DEBUG) {
System.err.println(getThreadName() + ": createContextARB-MapGLVersions Mapped "+GLContext.getGLVersion(_major[0], _minor[0], _ctp[0], null));
}
_ctx = createContextARBImpl(share, direct, _ctp[0], _major[0], _minor[0]);
if(0!=_ctx) {
if( !setGLFunctionAvailability(true, _major[0], _minor[0], _ctp[0], false /* strictMatch */, false /* withinGLVersionsMapping */) ) {
throw new InternalError("setGLFunctionAvailability !strictMatch failed");
}
}
}
return _ctx;
}
//----------------------------------------------------------------------
//
public static class MappedGLVersion {
public final AbstractGraphicsDevice device;
public final int reqMajorVersion;
public final int reqProfile;
public final VersionNumber ctxVersion;
public final int ctxOptions;
public final GLRendererQuirks quirks;
public final VersionNumber preCtxVersion;
public final int preCtxOptions;
public MappedGLVersion(final AbstractGraphicsDevice device, final int reqMajorVersion, final int reqProfile,
final VersionNumber ctxVersion, final int ctxOptions, final GLRendererQuirks quirks,
final VersionNumber preCtxVersion, final int preCtxOptions) {
this.device = device;
this.reqMajorVersion = reqMajorVersion;
this.reqProfile = reqProfile;
this.ctxVersion = ctxVersion;
this.ctxOptions = ctxOptions;
this.quirks = quirks;
this.preCtxVersion = preCtxVersion;
this.preCtxOptions = preCtxOptions;
}
@Override
public final String toString() {
return toString(new StringBuilder(), -1, -1, -1, -1).toString();
}
public final StringBuilder toString(final StringBuilder sb, final int minMajor, final int minMinor, final int maxMajor, final int maxMinor) {
sb.append(device.toString()).append(" ").append(reqMajorVersion).append(" (");
GLContext.getGLProfile(sb, reqProfile).append(")");
if( minMajor >=0 && minMinor >=0 && maxMajor >= 0 && maxMinor >= 0) {
sb.append("[").append(minMajor).append(".").append(minMinor).append(" .. ").append(maxMajor).append(".").append(maxMinor).append("]");
}
sb.append(": [");
if( null != preCtxVersion ) {
GLContext.getGLVersion(sb, preCtxVersion, preCtxOptions, null);
} else {
sb.append("None");
}
sb.append("] -> [");
GLContext.getGLVersion(sb, ctxVersion, ctxOptions, null).append("]");
return sb;
}
}
public static interface MappedGLVersionListener {
void glVersionMapped(final MappedGLVersion e);
}
private static MappedGLVersionListener mapGLVersionListener = null;
protected static synchronized void setMappedGLVersionListener(final MappedGLVersionListener mvl) {
mapGLVersionListener = mvl;
}
/**
* Called by {@link jogamp.opengl.GLContextImpl#createContextARBMapVersionsAvailable(int,int)} not intended to be used by
* implementations. However, if {@link jogamp.opengl.GLContextImpl#createContextARB(long, boolean)} is not being used within
* {@link com.jogamp.opengl.GLDrawableFactory#getOrCreateSharedContext(com.jogamp.nativewindow.AbstractGraphicsDevice)},
* GLProfile has to map the available versions.
*
* @param reqMajor Key Value either 1, 2, 3 or 4
* @param profile Key Value either {@link #CTX_PROFILE_COMPAT}, {@link #CTX_PROFILE_CORE} or {@link #CTX_PROFILE_ES}
* @param resVersion the resulting version number
* @param resCtp the resulting context options
* @return the old mapped value
*
* @see #createContextARBMapVersionsAvailable
*/
protected static MappedGLVersion mapAvailableGLVersion(final AbstractGraphicsDevice device,
final int reqMajor, final int profile,
final VersionNumber resVersion, final int resCtp,
final GLRendererQuirks resQuirks)
{
final Integer preVal = mapAvailableGLVersion(device, reqMajor, profile, resVersion, resCtp);
final int[] preCtp = { 0 };
final VersionNumber preVersion = null != preVal ? decomposeBits(preVal.intValue(), preCtp) : null;
final MappedGLVersion res = new MappedGLVersion(device, reqMajor, profile, resVersion, resCtp, resQuirks, preVersion, preCtp[0]);
if( null != mapGLVersionListener ) {
mapGLVersionListener.glVersionMapped(res);
}
return res;
}
private static Integer mapAvailableGLVersion(final AbstractGraphicsDevice device,
final int reqMajor, final int profile, final VersionNumber resVersion, int resCtp)
{
validateProfileBits(profile, "profile");
validateProfileBits(resCtp, "resCtp");
if( GLRendererQuirks.existStickyDeviceQuirk(device, GLRendererQuirks.NoFBOSupport) ) {
resCtp &= ~CTX_IMPL_FBO ;
}
if(DEBUG) {
System.err.println(getThreadName() + ": createContextARB-MapGLVersions MAP "+device+": "+reqMajor+" ("+GLContext.getGLProfile(new StringBuilder(), profile).toString()+ ") -> "+
getGLVersion(resVersion.getMajor(), resVersion.getMinor(), resCtp, null));
}
final String objectKey = getDeviceVersionAvailableKey(device, reqMajor, profile);
final Integer val = Integer.valueOf(composeBits(resVersion.getMajor(), resVersion.getMinor(), resCtp));
synchronized(deviceVersionAvailable) {
return deviceVersionAvailable.put( objectKey, val );
}
}
/**
* Remaps all available GL Version from {@code fromDevice} to {@code toDevice}.
*
* @param fromDevice the required matching device key to be mapped
* @param toDevice mapped GL version target
* @param overwrite if {@code true} overwrites previous mapping, otherwise leaves it untouched
* @param ctpCriteria the given GL Version context profile required to map a {@code fromDevice} GL Version.
* To map all GL Versions, just pass {@link GLContext#CTX_PROFILE_ES} | {@link GLContext#CTX_PROFILE_CORE} | {@link GLContext#CTX_PROFILE_COMPAT}
*/
protected static void remapAvailableGLVersions(final AbstractGraphicsDevice fromDevice, final AbstractGraphicsDevice toDevice,
final boolean overwrite, final int ctpCriteria) {
if( fromDevice == toDevice || fromDevice.getUniqueID() == toDevice.getUniqueID() ) {
return; // NOP
}
synchronized(deviceVersionAvailable) {
if(DEBUG) {
System.err.println(getThreadName() + ": createContextARB-MapGLVersions REMAP "+fromDevice+" -> "+toDevice+
", overwrite "+overwrite+", ctpCriteria "+getGLProfile(new StringBuilder(), ctpCriteria).toString());
}
final IdentityHashMap newDeviceVersionAvailable = new IdentityHashMap();
final Set keys = deviceVersionAvailable.keySet();
for(final Iterator keyI = keys.iterator(); keyI.hasNext(); ) {
final String origKey = keyI.next();
final Integer valI = deviceVersionAvailable.get(origKey);
if( null != valI ) {
if(DEBUG) {
final int[] ctp = { 0 };
final VersionNumber version = decomposeBits(valI.intValue(), ctp);
System.err.println(" MapGLVersions REMAP VAL0 "+origKey+" == "+GLContext.getGLVersion(new StringBuilder(), version, ctp[0], null).toString());
}
newDeviceVersionAvailable.put(origKey, valI);
final int devSepIdx = origKey.lastIndexOf('-');
if( 0 >= devSepIdx ) {
throw new InternalError("device-separator '-' at "+devSepIdx+" of "+origKey);
}
final String devUniqueID = origKey.substring(0, devSepIdx);
if( fromDevice.getUniqueID().equals(devUniqueID) ) {
// key/val pair from 'fromDevice' to be mapped to 'toDevice'
final String profileReq = origKey.substring(devSepIdx);
final String newKey = (toDevice.getUniqueID()+profileReq).intern();
final Integer preI = deviceVersionAvailable.get(newKey);
final int valCTP = getCTPFromBits(valI.intValue());
final boolean write = ( overwrite || null == preI ) && 0 != ( ctpCriteria & valCTP );
if( write ) {
newDeviceVersionAvailable.put(newKey, valI);
}
if(DEBUG) {
if( write ) {
System.err.println(" MapGLVersions REMAP NEW0 "+newKey+" -> (ditto)");
} else {
System.err.println(" MapGLVersions REMAP NEW0 "+newKey+" (unchanged)");
}
if( null != preI ) {
final int[] ctp = { 0 };
final VersionNumber version = decomposeBits(preI.intValue(), ctp);
System.err.println(" MapGLVersions REMAP OLD1 "+newKey+" :: "+GLContext.getGLVersion(new StringBuilder(), version, ctp[0], null).toString());
} else {
System.err.println(" MapGLVersions REMAP OLD1 "+newKey+" :: (nil)");
}
}
}
}
}
deviceVersionAvailable.clear();
deviceVersionAvailable.putAll(newDeviceVersionAvailable);
GLContext.setAvailableGLVersionsSet(toDevice, true);
}
}
private final boolean mapGLVersions(final AbstractGraphicsDevice device) {
synchronized (GLContext.deviceVersionAvailable) {
final boolean hasOpenGLESSupport = drawable.getFactory().hasOpenGLESSupport();
final boolean hasOpenGLDesktopSupport = drawable.getFactory().hasOpenGLDesktopSupport();
final boolean hasMinorVersionSupport = drawable.getFactoryImpl().hasMajorMinorCreateContextARB();
if (DEBUG) {
System.err.println(getThreadName() + ": createContextARB-MapGLVersions START (GLDesktop "+hasOpenGLDesktopSupport+", GLES "+hasOpenGLESSupport+", minorVersion "+hasMinorVersionSupport+") on "+device);
}
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 }
boolean hasGL4bc = false;
boolean hasGL3bc = false;
boolean hasGL2 = false;
boolean hasGL4 = false;
boolean hasGL3 = false;
boolean hasES3 = false;
boolean hasES2 = false;
boolean hasES1 = false;
if( hasOpenGLESSupport && !GLProfile.disableOpenGLES ) {
if( !hasES3) {
hasES3 = createContextARBMapVersionsAvailable(device, 3, CTX_PROFILE_ES, hasMinorVersionSupport); // ES3
success |= hasES3;
if( hasES3 ) {
if( 0 == ( CTX_IMPL_ACCEL_SOFT & ctxOptions ) ) {
// Map hw-accel ES3 to all lower core profiles: ES2
mapAvailableGLVersion(device, 2, CTX_PROFILE_ES, ctxVersion, ctxOptions, glRendererQuirks);
if( PROFILE_ALIASING ) {
hasES2 = true;
}
}
resetStates(false); // clean context states, since creation was temporary
}
}
if( !hasES2) {
hasES2 = createContextARBMapVersionsAvailable(device, 2, CTX_PROFILE_ES, hasMinorVersionSupport); // ES2
success |= hasES2;
if( hasES2 ) {
if( ctxVersion.getMajor() >= 3 && hasRendererQuirk(GLRendererQuirks.GLES3ViaEGLES2Config)) {
mapAvailableGLVersion(device, 3, CTX_PROFILE_ES, ctxVersion, ctxOptions, glRendererQuirks);
}
resetStates(false); // clean context states, since creation was temporary
}
}
if( !hasES1) {
hasES1 = createContextARBMapVersionsAvailable(device, 1, CTX_PROFILE_ES, hasMinorVersionSupport); // ES1
success |= hasES1;
if( hasES1 ) {
resetStates(false); // clean context states, since creation was temporary
}
}
}
// 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( hasOpenGLDesktopSupport && !GLProfile.disableOpenGLDesktop && !GLProfile.disableOpenGLCore && !hasGL4 && !hasGL3 ) {
hasGL3 = createContextARBMapVersionsAvailable(device, 3, CTX_PROFILE_CORE, hasMinorVersionSupport); // 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.
mapAvailableGLVersion(device, 4, CTX_PROFILE_CORE, ctxVersion, ctxOptions, glRendererQuirks);
hasGL4 = true;
if(DEBUG) {
System.err.println(getThreadName() + ": createContextARB-MapGLVersions: Quirk Triggerd: "+GLRendererQuirks.toString(GLRendererQuirks.GL4NeedsGL3Request)+": cause: OS "+Platform.getOSType()+", OS Version "+Platform.getOSVersionNumber());
}
}
resetStates(false); // clean the context states, since creation was temporary
}
}
}
if( hasOpenGLDesktopSupport && !GLProfile.disableOpenGLDesktop && !GLProfile.disableOpenGLCore ) {
if( !hasGL4 ) {
hasGL4 = createContextARBMapVersionsAvailable(device, 4, CTX_PROFILE_CORE, hasMinorVersionSupport); // GL4
success |= hasGL4;
if( hasGL4 ) {
if( 0 == ( CTX_IMPL_ACCEL_SOFT & ctxOptions ) ) {
// Map hw-accel GL4 to all lower core profiles: GL3
mapAvailableGLVersion(device, 3, CTX_PROFILE_CORE, ctxVersion, ctxOptions, glRendererQuirks);
if( PROFILE_ALIASING ) {
hasGL3 = true;
}
}
resetStates(false); // clean context states, since creation was temporary
}
}
if( !hasGL3 ) {
hasGL3 = createContextARBMapVersionsAvailable(device, 3, CTX_PROFILE_CORE, hasMinorVersionSupport); // GL3
success |= hasGL3;
if( hasGL3 ) {
resetStates(false); // clean this context states, since creation was temporary
}
}
}
if( hasOpenGLDesktopSupport && !GLProfile.disableOpenGLDesktop ) {
if( !hasGL4bc ) {
hasGL4bc = createContextARBMapVersionsAvailable(device, 4, CTX_PROFILE_COMPAT, hasMinorVersionSupport); // GL4bc
success |= hasGL4bc;
if( hasGL4bc ) {
if( !hasGL4 ) { // last chance .. ignore hw-accel
mapAvailableGLVersion(device, 4, CTX_PROFILE_CORE, ctxVersion, ctxOptions, glRendererQuirks);
hasGL4 = true;
}
if( !hasGL3 ) { // last chance .. ignore hw-accel
mapAvailableGLVersion(device, 3, CTX_PROFILE_CORE, ctxVersion, ctxOptions, glRendererQuirks);
hasGL3 = true;
}
if( 0 == ( CTX_IMPL_ACCEL_SOFT & ctxOptions ) ) {
// Map hw-accel GL4bc to all lower compatible profiles: GL3bc, GL2
mapAvailableGLVersion(device, 3, CTX_PROFILE_COMPAT, ctxVersion, ctxOptions, glRendererQuirks);
mapAvailableGLVersion(device, 2, CTX_PROFILE_COMPAT, ctxVersion, ctxOptions, glRendererQuirks);
if(PROFILE_ALIASING) {
hasGL3bc = true;
hasGL2 = true;
}
}
resetStates(false); // clean this context states, since creation was temporary
}
}
if( !hasGL3bc ) {
hasGL3bc = createContextARBMapVersionsAvailable(device, 3, CTX_PROFILE_COMPAT, hasMinorVersionSupport); // GL3bc
success |= hasGL3bc;
if( hasGL3bc ) {
if(!hasGL3) { // last chance .. ignore hw-accel
mapAvailableGLVersion(device, 3, CTX_PROFILE_CORE, ctxVersion, ctxOptions, glRendererQuirks);
hasGL3 = true;
}
if( 0 == ( CTX_IMPL_ACCEL_SOFT & ctxOptions ) ) {
// Map hw-accel GL3bc to all lower compatible profiles: GL2
mapAvailableGLVersion(device, 2, CTX_PROFILE_COMPAT, ctxVersion, ctxOptions, glRendererQuirks);
if(PROFILE_ALIASING) {
hasGL2 = true;
}
}
resetStates(false); // clean this context states, since creation was temporary
}
}
if( !hasGL2 ) {
hasGL2 = createContextARBMapVersionsAvailable(device, 2, CTX_PROFILE_COMPAT, hasMinorVersionSupport); // 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, true);
}
if(DEBUG) {
final long t1 = System.nanoTime();
System.err.println(getThreadName() + ": createContextARB-MapGLVersions END (success "+success+") on "+device+", profileAliasing: "+PROFILE_ALIASING+", total "+(t1-t0)/1e6 +"ms");
if( success ) {
System.err.println(GLContext.dumpAvailableGLVersions(null).toString());
}
}
return success;
}
}
/**
* 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(final AbstractGraphicsDevice device, final int reqMajor, final int reqProfile,
final boolean hasMinorVersionSupport) {
long _context;
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,
// so the user can always cast to the highest available one.
int maxMajor, maxMinor;
int minMajor, minMinor;
final int major[] = new int[1];
final int minor[] = new int[1];
if( hasMinorVersionSupport ) {
if( CTX_PROFILE_ES == reqProfile ) {
// ES3, ES2 or ES1
maxMajor=reqMajor; maxMinor=GLContext.getMaxMinor(ctp, maxMajor);
minMajor=reqMajor; minMinor=0;
} else {
if( 4 == reqMajor ) {
maxMajor=4; maxMinor=GLContext.getMaxMinor(ctp, maxMajor);
minMajor=4; minMinor=0;
} else if( 3 == reqMajor ) {
maxMajor=3; maxMinor=GLContext.getMaxMinor(ctp, maxMajor);
minMajor=3; minMinor=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
maxMajor=3; maxMinor=0;
minMajor=2; minMinor=0;
}
}
} else {
if( CTX_PROFILE_ES == reqProfile ) {
// ES3, ES2 or ES1
maxMajor=reqMajor; maxMinor=0;
minMajor=reqMajor; minMinor=0;
} else {
if( 4 == reqMajor ) {
maxMajor=4; maxMinor=0;
minMajor=4; minMinor=0;
} else if( 3 == reqMajor ) {
maxMajor=3; maxMinor=1;
minMajor=3; minMinor=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
maxMajor=2; maxMinor=0;
minMajor=2; minMinor=0;
}
}
}
_context = createContextARBVersions(0, true, ctp,
/* max */ maxMajor, maxMinor,
/* min */ minMajor, minMinor,
/* res */ major, minor);
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,
/* max */ maxMajor, maxMinor,
/* min */ minMajor, minMinor,
/* res */ major, minor);
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,
/* max */ maxMajor, maxMinor,
/* min */ minMajor, minMinor,
/* res */ major, minor);
}
}
final boolean res;
if( 0 != _context ) {
// ctxMajorVersion, ctxMinorVersion, ctxOptions is being set by
// createContextARBVersions(..) -> setGLFunctionAvailbility(..) -> setContextVersion(..)
final MappedGLVersion me = mapAvailableGLVersion(device, reqMajor, reqProfile, ctxVersion, ctxOptions, glRendererQuirks);
destroyContextARBImpl(_context);
if (DEBUG) {
System.err.println(getThreadName() + ": createContextARB-MapGLVersions HAVE "+me.toString(new StringBuilder(), minMajor, minMinor, maxMajor, maxMinor).toString());
}
res = true;
} else {
if (DEBUG) {
System.err.println(getThreadName() + ": createContextARB-MapGLVersions NOPE "+device+", "+reqMajor+" ("+GLContext.getGLProfile(new StringBuilder(), reqProfile).toString()+ ") ["+maxMajor+"."+maxMinor+" .. "+minMajor+"."+minMinor+"]");
}
res = false;
}
return res;
}
private final long createContextARBVersions(final long share, final boolean direct, final int ctxOptionFlags,
final int maxMajor, final int maxMinor,
final int minMajor, final int minMinor,
final int major[], final int minor[]) {
major[0]=maxMajor;
minor[0]=maxMinor;
long _context=0;
int i=0;
do {
if (DEBUG) {
i++;
System.err.println(getThreadName() + ": createContextARBVersions."+i+": share "+share+", direct "+direct+
", version "+major[0]+"."+minor[0]+" ["+maxMajor+"."+maxMinor+" .. "+minMajor+"."+minMinor+"]");
}
_context = createContextARBImpl(share, direct, ctxOptionFlags, major[0], minor[0]);
if(0 != _context) {
if( setGLFunctionAvailability(true, major[0], minor[0], ctxOptionFlags, true /* strictMatch */, true /* withinGLVersionsMapping */) ) {
break;
} else {
destroyContextARBImpl(_context);
_context = 0;
}
}
} while ( ( major[0]>minMajor || major[0]==minMajor && minor[0] >minMinor ) && // #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]+" ["+maxMajor+"."+maxMinor+" .. "+minMajor+"."+minMinor+"]");
}
return _context;
}
//----------------------------------------------------------------------
// Managing the actual OpenGL version, usually figured at creation time.
// As a last resort, the GL_VERSION string may be used ..
//
/**
* If major > 0 || minor > 0 : Use passed values, determined at creation time
* Otherwise .. don't touch ..
*/
private final void setContextVersion(final int major, final int minor, final int ctp, final VersionNumberString glVendorVersion, final boolean useGL) {
if ( 0 == ctp ) {
throw new GLException("Invalid GL Version "+major+"."+minor+", ctp "+toHexString(ctp));
}
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 : glGetStringInt(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);
}
}
}
}
//----------------------------------------------------------------------
// Helpers for various context implementations
//
private final boolean verifyInstance(final GLProfile glp, final String suffix, final Object instance) {
return ReflectionUtil.instanceOf(instance, glp.getGLImplBaseClassName()+suffix);
}
private final Object createInstance(final AbstractGraphicsDevice adevice, final int majorVersion, final int minorVersion, final int contextOption,
final boolean glObject, final Object[] cstrArgs) {
final String profileString = GLContext.getGLProfile(majorVersion, minorVersion, contextOption);
final GLProfile glp = GLProfile.get(adevice, profileString) ;
return ReflectionUtil.createInstance(glp.getGLCtor(glObject), cstrArgs);
}
private final boolean verifyInstance(final AbstractGraphicsDevice adevice, final int majorVersion, final int minorVersion, final int contextOption,
final String suffix, final Object instance) {
final String profileString = GLContext.getGLProfile(majorVersion, minorVersion, contextOption);
final GLProfile glp = GLProfile.get(adevice, profileString) ;
return ReflectionUtil.instanceOf(instance, glp.getGLImplBaseClassName()+suffix);
}
/**
* Create the GL instance for this context,
* requires valid {@link #getGLProcAddressTable()} result!
*/
private final GL createGL(final AbstractGraphicsDevice adevice, final int majorVersion, final int minorVersion, final int contextOption) {
final String profileString = GLContext.getGLProfile(majorVersion, minorVersion, contextOption);
final GLProfile glp = GLProfile.get(adevice, profileString);
final GL gl = (GL) ReflectionUtil.createInstance(glp.getGLCtor(true), new Object[] { glp, this });
//nal GL gl = (GL) createInstance(glp, true, new Object[] { glp, this } );
/* FIXME: refactor dependence on Java 2D / JOGL bridge
if (tracker != null) {
gl.setObjectTracker(tracker);
}
*/
return gl;
}
/**
* Finalizes GL instance initialization after this context has been initialized.
*
* Method calls 'void finalizeInit()' of instance 'gl' as retrieved by reflection, if exist.
*
*/
private void finalizeInit(final GL gl) {
Method finalizeInit = null;
try {
finalizeInit = ReflectionUtil.getMethod(gl.getClass(), "finalizeInit", new Class>[]{ });
} catch ( final Throwable t ) {
if(DEBUG) {
System.err.println("Caught "+t.getClass().getName()+": "+t.getMessage());
t.printStackTrace();
}
}
if( null != finalizeInit ) {
ReflectionUtil.callMethod(gl, finalizeInit, new Object[]{ });
} else {
throw new InternalError("Missing 'void finalizeInit(ProcAddressTable)' in "+gl.getClass().getName());
}
}
public final ProcAddressTable getGLProcAddressTable() {
return glProcAddressTable;
}
/**
* Shall return the platform extension ProcAddressTable,
* ie for GLXExt, EGLExt, ..
*/
public abstract ProcAddressTable getPlatformExtProcAddressTable();
/** Maps the given "platform-independent" function name to a real function
name. Currently not used. */
protected final String mapToRealGLFunctionName(final String glFunctionName) {
final Map map = getFunctionNameMap();
if( null != map ) {
final String lookup = map.get(glFunctionName);
if (lookup != null) {
return lookup;
}
}
return glFunctionName;
}
protected abstract Map getFunctionNameMap() ;
/** 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."
*/
protected final String mapToRealGLExtensionName(final String glExtensionName) {
final Map map = getExtensionNameMap();
if( null != map ) {
final String lookup = map.get(glExtensionName);
if (lookup != null) {
return lookup;
}
}
return glExtensionName;
}
protected abstract Map getExtensionNameMap() ;
/**
* Returns the DynamicLookupHelper
*/
public final GLDynamicLookupHelper getGLDynamicLookupHelper() {
return drawable.getFactoryImpl().getGLDynamicLookupHelper( ctxVersion.getMajor(), ctxOptions );
}
public final GLDynamicLookupHelper getGLDynamicLookupHelper(final int majorVersion, final int contextOptions) {
return drawable.getFactoryImpl().getGLDynamicLookupHelper( majorVersion, contextOptions );
}
/** Helper routine which resets a ProcAddressTable generated by the
GLEmitter by looking up anew all of its function pointers
using the given {@link GLDynamicLookupHelper}. */
protected final void resetProcAddressTable(final ProcAddressTable table, final GLDynamicLookupHelper dlh) {
SecurityUtil.doPrivileged(new PrivilegedAction