/*
* 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.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.ExceptionUtils;
import com.jogamp.common.os.DynamicLookupHelper;
import com.jogamp.common.os.Platform;
import com.jogamp.common.util.ReflectionUtil;
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 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.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;
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;
// 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;
private boolean pixelDataEvaluated;
private int /* pixelDataInternalFormat, */ pixelDataFormat, pixelDataType;
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.hasCreatedSharedLeft(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;
if ( !isInit && null != boundFBOTarget ) { // : boundFBOTarget is not written yet
boundFBOTarget[0] = 0; // draw
boundFBOTarget[1] = 0; // read
}
pixelDataEvaluated = false;
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 old = drawable;
if( isCreated() && null != old && old.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() ) {
makeCurrent(true); // implicit: associateDrawable(true)
if( !lockHeld ) {
release(false);
}
}
return old;
}
@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.dumpStackTrace(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.hasCreatedSharedLeft(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;
//----------------------------------------------------------------------
//
/**
* {@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() ) {
if ( 0 == drawable.getHandle() ) {
throw new GLException("drawable has invalid handle: "+drawable);
}
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]: 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 (res != CONTEXT_NOT_CURRENT) { // still locked!
setCurrent(this);
if(res == CONTEXT_CURRENT_NEW) {
// check if the drawable's and the GL's GLProfile are equal
// throws an GLException if not
drawable.getGLProfile().verifyEquality(gl.getGLProfile());
glDebugHandler.init( isGL2GL3() && isGLDebugEnabled() );
if(DEBUG_GL) {
setGL( GLPipelineFactory.create("javax.media.opengl.Debug", null, gl, null) );
if(glDebugHandler.isEnabled()) {
glDebugHandler.addListener(new GLDebugMessageHandler.StdErrGLDebugListener(true));
}
}
if(TRACE_GL) {
setGL( GLPipelineFactory.create("javax.media.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();
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 != sharedMaster ) {
sharedMaster.drawable.unlockSurface();
}
}
if ( DEBUG_TRACE_SWITCH ) {
System.err.println(getThreadName() + ": Create GL context "+(created?"OK":"FAILED")+": For " + getClass().getName()+" - "+getGLVersion()+" - "+getTraceSwitchMsg());
// ExceptionUtils.dumpStackTrace(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;
}
GLContext.mapAvailableGLVersion(device, reqMajor, reqProfile, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions);
// Perform all required profile mappings
if( isCompat ) {
// COMPAT via non ARB
GLContext.mapAvailableGLVersion(device, reqMajor, GLContext.CTX_PROFILE_CORE, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions);
if( reqMajor >= 4 ) {
GLContext.mapAvailableGLVersion(device, 3, reqProfile, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions);
GLContext.mapAvailableGLVersion(device, 3, GLContext.CTX_PROFILE_CORE, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions);
}
if( reqMajor >= 3 ) {
GLContext.mapAvailableGLVersion(device, 2, reqProfile, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions);
}
} else {
// CORE via non ARB, unlikely, however ..
if( reqMajor >= 4 ) {
GLContext.mapAvailableGLVersion(device, 3, reqProfile, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions);
}
}
GLContext.setAvailableGLVersionsSet(device);
if (DEBUG) {
System.err.println(getThreadName() + ": createContextOLD-MapVersionsAvailable HAVE: " + device+" -> "+reqMajor+"."+reqProfile+ " -> "+getGLVersion());
}
}
}
}
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.
*
* @param sharedWithHandle the shared context handle or 0
* @return true if successful, or false
* @throws GLException
*/
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);
/**
* 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)
{
if( GLProfile.disableOpenGLARBContext ) {
return 0;
}
final AbstractGraphicsConfiguration config = drawable.getNativeSurface().getGraphicsConfiguration();
final AbstractGraphicsDevice device = config.getScreen().getDevice();
if (DEBUG) {
System.err.println(getThreadName() + ": createContextARB: mappedVersionsAvailableSet("+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 GLCapabilitiesImmutable glCaps = (GLCapabilitiesImmutable) config.getChosenCapabilities();
final int[] reqMajorCTP = new int[] { 0, 0 };
GLContext.getRequestMajorAndCompat(glCaps.getGLProfile(), reqMajorCTP);
if(DEBUG) {
System.err.println(getThreadName() + ": createContextARB: Requested "+GLContext.getGLVersion(reqMajorCTP[0], 0, reqMajorCTP[0], 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: 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;
}
private final boolean mapGLVersions(final 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 }
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( !GLProfile.disableOpenGLCore && !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( !GLProfile.disableOpenGLCore ) {
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;
}
}
/**
* 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 int reqMajor, final int reqProfile) {
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 majorMax, minorMax;
int majorMin, minorMin;
final int major[] = new int[1];
final int minor[] = new int[1];
if( 4 == reqMajor ) {
majorMax=4; minorMax=GLContext.getMaxMinor(ctp, majorMax);
majorMin=4; minorMin=0;
} else if( 3 == reqMajor ) {
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
majorMax=3; minorMax=0;
majorMin=2; minorMin=0;
}
_context = createContextARBVersions(0, true, ctp,
/* max */ majorMax, minorMax,
/* min */ majorMin, minorMin,
/* 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 */ majorMax, minorMax,
/* min */ majorMin, minorMin,
/* 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 */ majorMax, minorMax,
/* min */ majorMin, minorMin,
/* res */ major, minor);
}
}
final boolean res;
if( 0 != _context ) {
final AbstractGraphicsDevice device = drawable.getNativeSurface().getGraphicsConfiguration().getScreen().getDevice();
// ctxMajorVersion, ctxMinorVersion, ctxOptions is being set by
// createContextARBVersions(..) -> setGLFunctionAvailbility(..) -> setContextVersion(..)
GLContext.mapAvailableGLVersion(device, reqMajor, reqProfile, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions);
destroyContextARBImpl(_context);
if (DEBUG) {
System.err.println(getThreadName() + ": createContextARB-MapVersionsAvailable HAVE: " +reqMajor+"."+reqProfile+ " -> "+getGLVersion());
}
res = true;
} else {
if (DEBUG) {
System.err.println(getThreadName() + ": createContextARB-MapVersionsAvailable NOPE: "+reqMajor+"."+reqProfile);
}
res = false;
}
return res;
}
private final long createContextARBVersions(final long share, final boolean direct, final int ctxOptionFlags,
final int majorMax, final int minorMax,
final int majorMin, final int minorMin,
final int major[], final int minor[]) {
major[0]=majorMax;
minor[0]=minorMax;
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]+", major["+majorMin+".."+majorMax+"], minor["+minorMin+".."+minorMax+"]");
}
_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]>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;
}
//----------------------------------------------------------------------
// 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 : 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);
}
}
}
}
//----------------------------------------------------------------------
// Helpers for various context implementations
//
private Object createInstance(final GLProfile glp, final boolean glObject, final Object[] cstrArgs) {
return ReflectionUtil.createInstance(glp.getGLCtor(glObject), cstrArgs);
}
private boolean verifyInstance(final GLProfile glp, final String suffix, final Object instance) {
return ReflectionUtil.instanceOf(instance, glp.getGLImplBaseClassName()+suffix);
}
/** Create the GL for this context. */
protected GL createGL(final GLProfile glp) {
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);
}
*/
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[]{ });
}
}
public final ProcAddressTable getGLProcAddressTable() {
return glProcAddressTable;
}
/**
* Shall return the platform extension ProcAddressTable,
* ie for GLXExt, EGLExt, ..
*/
public abstract ProcAddressTable getPlatformExtProcAddressTable();
/**
* Part of GL_NV_vertex_array_range.
*
* Provides platform-independent access to the wglAllocateMemoryNV /
* glXAllocateMemoryNV.
*
*/
public abstract ByteBuffer glAllocateMemoryNV(int size, float readFrequency, float writeFrequency, float priority);
/**
* Part of GL_NV_vertex_array_range.
*
* Provides platform-independent access to the wglFreeMemoryNV /
* glXFreeMemoryNV.
*
*/
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
associated routines to wglAllocateMemoryNV / glXAllocateMemoryNV. */
protected final String mapToRealGLFunctionName(final String glFunctionName) {
final Map map = getFunctionNameMap();
final String lookup = ( null != map ) ? map.get(glFunctionName) : null;
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();
final String lookup = ( null != map ) ? map.get(glExtensionName) : null;
if (lookup != null) {
return lookup;
}
return glExtensionName;
}
protected abstract Map getExtensionNameMap() ;
/** Helper routine which resets a ProcAddressTable generated by the
GLEmitter by looking up anew all of its function pointers. */
protected final void resetProcAddressTable(final ProcAddressTable table) {
AccessController.doPrivileged(new PrivilegedAction