jogamp.opengl.GLContextImpl Maven / Gradle / Ivy
/*
* 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.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.FunctionAddressResolver;
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.GL2GL3;
import javax.media.opengl.GL3ES3;
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.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 creation and initialization of buffer objects to avoid
// repeated glGet calls upon glMapBuffer operations
private GLBufferSizeTracker bufferSizeTracker; // Singleton - Set by GLContextShareSet
private final GLBufferStateTracker bufferStateTracker = new GLBufferStateTracker();
private final GLStateTracker glStateTracker = new GLStateTracker();
private GLDebugMessageHandler glDebugHandler = null;
private final int[] boundFBOTarget = new int[] { 0, 0 }; // { draw, read }
private int defaultVAO = 0;
protected GLDrawableImpl drawable;
protected GLDrawableImpl drawableRead;
private volatile boolean pixelDataEvaluated;
private int /* pixelDataInternalFormat, */ pixelDataFormat, pixelDataType;
protected GL gl;
protected static final Object mappedContextTypeObjectLock;
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(GLDrawableImpl drawable, GLContext shareWith) {
super();
if (shareWith != null) {
GLContextShareSet.registerSharing(this, shareWith);
}
GLContextShareSet.synchronizeBufferObjectSharing(shareWith, this);
this.drawable = drawable;
this.drawableRead = drawable;
this.glDebugHandler = new GLDebugMessageHandler(this);
}
private final void clearStates() {
// Because we don't know how many other contexts we might be
// sharing with (and it seems too complicated to implement the
// GLObjectTracker's ref/unref scheme for the buffer-related
// optimizations), simply clear the cache of known buffers' sizes
// when we destroy contexts
if (bufferSizeTracker != null) {
bufferSizeTracker.clearCachedBufferSizes();
}
bufferStateTracker.clearBufferObjectState();
glStateTracker.setEnabled(false);
glStateTracker.clearStates();
}
@Override
protected void resetStates(boolean isInit) {
if( !isInit ) {
clearStates();
}
extensionAvailability = null;
glProcAddressTable = null;
gl = null;
contextFQN = null;
additionalCtxCreationFlags = 0;
glVendor = "";
glRenderer = glVendor;
glRendererLowerCase = glRenderer;
glVersion = glVendor;
if (boundFBOTarget != null) { //
boundFBOTarget[0] = 0; // draw
boundFBOTarget[1] = 0; // read
}
pixelDataEvaluated = false;
super.resetStates(isInit);
}
@Override
public final GLDrawable setGLReadDrawable(GLDrawable read) {
if(!isGLReadDrawableAvailable()) {
throw new GLException("Setting read drawable feature not available");
}
final boolean lockHeld = lock.isOwner(Thread.currentThread());
if(lockHeld) {
release();
} else if(lock.isLockedByOtherThread()) { // still could glitch ..
throw new GLException("GLContext current by other thread ("+lock.getOwner()+"), operation not allowed.");
}
final GLDrawable old = drawableRead;
drawableRead = ( null != read ) ? (GLDrawableImpl) read : drawable;
if(lockHeld) {
makeCurrent();
}
return old;
}
@Override
public final GLDrawable getGLReadDrawable() {
return drawableRead;
}
@Override
public final GLDrawable setGLDrawable(GLDrawable readWrite, boolean setWriteOnly) {
if( drawable == readWrite && ( setWriteOnly || drawableRead == readWrite ) ) {
return drawable; // no change.
}
final Thread currentThread = Thread.currentThread();
if( lock.isLockedByOtherThread() ) {
throw new GLException("GLContext current by other thread "+lock.getOwner().getName()+", operation not allowed on this thread "+currentThread.getName());
}
final boolean lockHeld = lock.isOwner(currentThread);
if( lockHeld && lock.getHoldCount() > 1 ) {
// would need to makeCurrent * holdCount
throw new GLException("GLContext is recursively locked - unsupported for setGLDrawable(..)");
}
final GLDrawableImpl old = drawable;
if( isCreated() && null != old && old.isRealized() ) {
if(!lockHeld) {
makeCurrent();
}
associateDrawable(false);
if(!lockHeld) {
release();
}
}
if(lockHeld) {
release();
}
if( !setWriteOnly || drawableRead == drawable ) { // if !setWriteOnly || !explicitReadDrawable
drawableRead = (GLDrawableImpl) readWrite;
}
drawableRetargeted |= null != drawable && readWrite != drawable;
drawable = (GLDrawableImpl) readWrite ;
if( isCreated() && null != drawable && drawable.isRealized() ) {
makeCurrent(true); // implicit: associateDrawable(true)
if( !lockHeld ) {
release();
}
}
return old;
}
@Override
public final GLDrawable getGLDrawable() {
return drawable;
}
public final GLDrawableImpl getDrawableImpl() {
return (GLDrawableImpl) getGLDrawable();
}
@Override
public final GL getRootGL() {
GL _gl = gl;
GL _parent = _gl.getDownstreamGL();
while ( null != _parent ) {
_gl = _parent;
_parent = _gl.getDownstreamGL();
}
return _gl;
}
@Override
public final GL getGL() {
return gl;
}
@Override
public GL setGL(GL gl) {
if(DEBUG) {
String sgl1 = (null!=this.gl)?this.gl.getClass().getSimpleName()+", "+this.gl.toString():"";
String sgl2 = (null!=gl)?gl.getClass().getSimpleName()+", "+gl.toString():"";
Exception e = new Exception("Info: setGL (OpenGL "+getGLVersion()+"): "+getThreadName()+", "+sgl1+" -> "+sgl2);
e.printStackTrace();
}
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 void release(boolean inDestruction) throws GLException {
if( TRACE_SWITCH ) {
System.err.println(getThreadName() +": GLContext.ContextSwitch[release.0]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+", inDestruction: "+inDestruction+", "+lock);
}
if ( !lock.isOwner(Thread.currentThread()) ) {
final String msg = getThreadName() +": Context not current on thread, obj " + toHexString(hashCode())+", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+", inDestruction: "+inDestruction+", "+lock;
if( DEBUG_TRACE_SWITCH ) {
System.err.println(msg);
if( null != lastCtxReleaseStack ) {
System.err.print("Last release call: ");
lastCtxReleaseStack.printStackTrace();
} else {
System.err.println("Last release call: NONE");
}
}
throw new GLException(msg);
}
Throwable drawableContextMadeCurrentException = null;
final boolean actualRelease = ( inDestruction || lock.getHoldCount() == 1 ) && 0 != contextHandle;
try {
if( actualRelease ) {
if( !inDestruction ) {
try {
contextMadeCurrent(false);
} catch (Throwable t) {
drawableContextMadeCurrentException = t;
}
}
releaseImpl();
}
} finally {
// exception prone ..
if( actualRelease ) {
setCurrent(null);
}
drawable.unlockSurface();
lock.unlock();
if( DEBUG_TRACE_SWITCH ) {
final String msg = getThreadName() +": GLContext.ContextSwitch[release.X]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+" - "+(actualRelease?"switch":"keep ")+" - "+lock;
lastCtxReleaseStack = new Throwable(msg);
if( TRACE_SWITCH ) {
System.err.println(msg);
// Thread.dumpStack();
}
}
}
if(null != drawableContextMadeCurrentException) {
throw new GLException("GLContext.release(false) during GLDrawableImpl.contextMadeCurrent(this, false)", drawableContextMadeCurrentException);
}
}
private Throwable lastCtxReleaseStack = null;
protected abstract void releaseImpl() throws GLException;
@Override
public final void destroy() {
if ( DEBUG_TRACE_SWITCH ) {
final long drawH = null != drawable ? drawable.getHandle() : 0;
System.err.println(getThreadName() + ": GLContextImpl.destroy.0: obj " + toHexString(hashCode()) + ", ctx " + toHexString(contextHandle) +
", surf "+toHexString(drawH)+", isShared "+GLContextShareSet.isShared(this)+" - "+lock);
}
if ( 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 ( lock.getHoldCount() > 2 ) {
final String msg = getThreadName() + ": GLContextImpl.destroy: obj " + toHexString(hashCode()) + ", ctx " + toHexString(contextHandle);
if ( DEBUG_TRACE_SWITCH ) {
System.err.println(msg+" - Lock was hold more than once - makeCurrent/release imbalance: "+lock);
Thread.dumpStack();
}
}
try {
// 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 (Throwable t) {
associateDrawableException = t;
}
if ( 0 != defaultVAO ) {
final int[] tmp = new int[] { defaultVAO };
final GL3ES3 gl3es3 = gl.getRootGL().getGL3ES3();
gl3es3.glBindVertexArray(0);
gl3es3.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: obj " + toHexString(hashCode()) + ", ctx " + toHexString(contextHandle) +
", isShared "+GLContextShareSet.isShared(this)+" - "+lock);
}
}
} finally {
drawable.unlockSurface();
}
if( null != associateDrawableException ) {
throw new GLException("Exception @ destroy's associateDrawable(false)", associateDrawableException);
}
} else {
resetStates(false);
}
}
protected abstract void destroyImpl() throws GLException;
@Override
public final void copy(GLContext source, int mask) throws GLException {
if (source.getHandle() == 0) {
throw new GLException("Source OpenGL context has not been created");
}
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:
*
* - {@link #makeCurrent} GLContextImpl
* - {@link #makeCurrentImpl} Platform Implementation
* - {@link #create} Platform Implementation
* - If
ARB_create_context
is supported:
*
* - {@link #createContextARB} GLContextImpl
* - {@link #createContextARBImpl} Platform Implementation
*
*
*
* 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 {
if( TRACE_SWITCH ) {
System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.0]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+" - "+lock);
}
// Note: the surface is locked within [makeCurrent .. swap .. release]
final int lockRes = drawable.lockSurface();
if (NativeSurface.LOCK_SURFACE_NOT_READY >= lockRes) {
if( DEBUG_TRACE_SWITCH ) {
System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.X1]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+" - Surface Not Ready - CONTEXT_NOT_CURRENT - "+lock);
}
return CONTEXT_NOT_CURRENT;
}
boolean unlockResources = true; // Must be cleared if successful, otherwise finally block will release context and/or surface!
int res = CONTEXT_NOT_CURRENT;
try {
if ( 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]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+" - keep - CONTEXT_CURRENT - "+lock);
}
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 (RuntimeException e) {
unlockResources = true;
throw e;
} finally {
if (unlockResources) {
if( DEBUG_TRACE_SWITCH ) {
System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.1]: Context lock.unlock() due to error, res "+makeCurrentResultToString(res)+", "+lock);
}
lock.unlock();
}
}
} /* if ( drawable.isRealized() ) */
} catch (RuntimeException e) {
unlockResources = true;
throw e;
} finally {
if (unlockResources) {
drawable.unlockSurface();
}
}
if (res != CONTEXT_NOT_CURRENT) {
setCurrent(this);
if(res == CONTEXT_CURRENT_NEW) {
// check if the drawable's and the GL's GLProfile are equal
// throws an GLException if not
getGLDrawable().getGLProfile().verifyEquality(gl.getGLProfile());
glDebugHandler.init( isGL2GL3() && isGLDebugEnabled() );
if(DEBUG_GL) {
gl = gl.getContext().setGL( GLPipelineFactory.create("javax.media.opengl.Debug", null, gl, null) );
if(glDebugHandler.isEnabled()) {
glDebugHandler.addListener(new GLDebugMessageHandler.StdErrGLDebugListener(true));
}
}
if(TRACE_GL) {
gl = gl.getContext().setGL( GLPipelineFactory.create("javax.media.opengl.Trace", null, gl, new Object[] { System.err } ) );
}
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]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+" - switch - "+makeCurrentResultToString(res)+" - stateTracker.on "+glStateTracker.isEnabled()+" - "+lock);
}
return res;
}
private final int makeCurrentWithinLock(int surfaceLockRes) throws GLException {
if (!isCreated()) {
if( 0 >= drawable.getWidth() || 0 >= drawable.getHeight() ) {
if ( DEBUG_TRACE_SWITCH ) {
System.err.println(getThreadName() + ": Create GL context REJECTED (zero surface size) obj " + toHexString(hashCode()) + ", surf "+toHexString(drawable.getHandle())+" for " + getClass().getName());
System.err.println(drawable.toString());
}
return CONTEXT_NOT_CURRENT;
}
if(DEBUG_GL) {
// only impacts w/ createContextARB(..)
additionalCtxCreationFlags |= GLContext.CTX_OPTION_DEBUG ;
}
final GLContextImpl shareWith = (GLContextImpl) GLContextShareSet.getShareContext(this);
if (null != shareWith) {
shareWith.getDrawableImpl().lockSurface();
}
final boolean created;
try {
created = createImpl(shareWith); // may throws exception if fails!
if( created && hasNoDefaultVAO() ) {
final int[] tmp = new int[1];
final GL3ES3 gl3es3 = gl.getRootGL().getGL3ES3();
gl3es3.glGenVertexArrays(1, tmp, 0);
defaultVAO = tmp[0];
gl3es3.glBindVertexArray(defaultVAO);
}
} finally {
if (null != shareWith) {
shareWith.getDrawableImpl().unlockSurface();
}
}
if ( DEBUG_TRACE_SWITCH ) {
if(created) {
System.err.println(getThreadName() + ": Create GL context OK: obj " + toHexString(hashCode()) + ", ctx " + toHexString(contextHandle) + ", surf "+toHexString(drawable.getHandle())+" for " + getClass().getName()+" - "+getGLVersion());
// Thread.dumpStack();
} else {
System.err.println(getThreadName() + ": Create GL context FAILED obj " + toHexString(hashCode()) + ", surf "+toHexString(drawable.getHandle())+" for " + getClass().getName());
}
}
if(!created) {
return CONTEXT_NOT_CURRENT;
}
// finalize mapping the available GLVersions, in case it's not done yet
{
final AbstractGraphicsConfiguration config = drawable.getNativeSurface().getGraphicsConfiguration();
final AbstractGraphicsDevice device = config.getScreen().getDevice();
// Non ARB desktop profiles may not have been registered
if( !GLContext.getAvailableGLVersionsSet(device) ) { // not yet set
if( 0 == ( ctxOptions & GLContext.CTX_PROFILE_ES) ) { // not ES profile
final int reqMajor;
final int reqProfile;
if( ctxVersion.compareTo(Version300) <= 0 ) {
reqMajor = 2;
} else {
reqMajor = ctxVersion.getMajor();
}
if( 0 != ( ctxOptions & GLContext.CTX_PROFILE_CORE) ) {
reqProfile = GLContext.CTX_PROFILE_CORE;
} else {
reqProfile = GLContext.CTX_PROFILE_COMPAT;
}
GLContext.mapAvailableGLVersion(device, reqMajor, reqProfile,
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(boolean bound) {
drawable.associateContext(this, bound);
}
/**
* Calls {@link GLDrawableImpl#contextMadeCurrent(GLContext, boolean)}
*/
protected void contextMadeCurrent(boolean current) {
drawable.contextMadeCurrent(this, current);
}
/**
* Platform dependent entry point for context creation.
*
* 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 share the shared context or null
* @return the valid and current context if successful, or null
* @throws GLException
*/
protected abstract boolean createImpl(GLContextImpl sharedWith) throws GLException ;
/**
* Platform dependent but harmonized implementation of the ARB_create_context
* mechanism to create a context.
*
* This method is called from {@link #createContextARB}, {@link #createImpl(GLContextImpl)} .. {@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)
{
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 };
getRequestMajorAndCompat(glCaps.getGLProfile(), reqMajorCTP);
int _major[] = { 0 };
int _minor[] = { 0 };
int _ctp[] = { 0 };
long _ctx = 0;
if( GLContext.getAvailableGLVersion(device, reqMajorCTP[0], reqMajorCTP[1],
_major, _minor, _ctp)) {
_ctp[0] |= additionalCtxCreationFlags;
_ctx = createContextARBImpl(share, direct, _ctp[0], _major[0], _minor[0]);
if(0!=_ctx) {
setGLFunctionAvailability(true, _major[0], _minor[0], _ctp[0], false);
}
}
return _ctx;
}
private final boolean mapGLVersions(AbstractGraphicsDevice device) {
synchronized (GLContext.deviceVersionAvailable) {
final long t0 = ( DEBUG ) ? System.nanoTime() : 0;
boolean success = false;
// Following GLProfile.GL_PROFILE_LIST_ALL order of profile detection { GL4bc, GL3bc, GL2, GL4, GL3, GL2GL3, GLES2, GL2ES2, GLES1, GL2ES1 }
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(!hasGL4) {
hasGL4 = createContextARBMapVersionsAvailable(4, CTX_PROFILE_CORE); // GL4
success |= hasGL4;
if(hasGL4) {
// Map all lower compatible 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) {
// Map all lower compatible profiles: GL3bc, GL2, GL4, GL3
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(!hasGL4) {
GLContext.mapAvailableGLVersion(device, 4, CTX_PROFILE_CORE, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions);
}
if(!hasGL3) {
GLContext.mapAvailableGLVersion(device, 3, CTX_PROFILE_CORE, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions);
}
if(PROFILE_ALIASING) {
hasGL3bc = true;
hasGL2 = true;
hasGL4 = true;
hasGL3 = true;
}
resetStates(false); // clean this context states, since creation was temporary
}
}
if(!hasGL3bc) {
hasGL3bc = createContextARBMapVersionsAvailable(3, CTX_PROFILE_COMPAT); // GL3bc
success |= hasGL3bc;
if(hasGL3bc) {
// Map all lower compatible profiles: GL2 and GL3
GLContext.mapAvailableGLVersion(device, 2, CTX_PROFILE_COMPAT, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions);
if(!hasGL3) {
GLContext.mapAvailableGLVersion(device, 3, CTX_PROFILE_CORE, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions);
}
if(PROFILE_ALIASING) {
hasGL2 = true;
hasGL3 = 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(int reqMajor, 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;
int major[] = new int[1];
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 ) {
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(long share, boolean direct, int ctxOptionFlags,
int majorMax, int minorMax,
int majorMin, int minorMin,
int major[], int minor[]) {
major[0]=majorMax;
minor[0]=minorMax;
long _context=0;
do {
if (DEBUG) {
System.err.println(getThreadName() + ": createContextARBVersions: share "+share+", direct "+direct+", version "+major[0]+"."+minor[0]);
}
_context = createContextARBImpl(share, direct, ctxOptionFlags, major[0], minor[0]);
if(0 != _context) {
if( setGLFunctionAvailability(true, major[0], minor[0], ctxOptionFlags, true) ) {
break;
} else {
destroyContextARBImpl(_context);
_context = 0;
}
}
} while ( ( major[0]>majorMin || major[0]==majorMin && minor[0] >=minorMin ) &&
GLContext.decrementGLVersion(ctxOptionFlags, major, minor) );
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(int major, int minor, int ctp, VersionNumberString glVendorVersion, 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(GLProfile glp, String suffix, Class>[] cstrArgTypes, Object[] cstrArgs) {
return ReflectionUtil.createInstance(glp.getGLImplBaseClassName()+suffix, cstrArgTypes, cstrArgs, getClass().getClassLoader());
}
private boolean verifyInstance(GLProfile glp, String suffix, Object instance) {
return ReflectionUtil.instanceOf(instance, glp.getGLImplBaseClassName()+suffix);
}
/** Create the GL for this context. */
protected GL createGL(GLProfile glp) {
final GL gl = (GL) createInstance(glp, "Impl", new Class[] { GLProfile.class, GLContextImpl.class }, 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(GL gl) {
Method finalizeInit = null;
try {
finalizeInit = ReflectionUtil.getMethod(gl.getClass(), "finalizeInit", new Class>[]{ });
} catch ( Throwable t ) {
if(DEBUG) {
System.err.println("Catched "+t.getClass().getName()+": "+t.getMessage());
t.printStackTrace();
}
}
if( null != finalizeInit ) {
ReflectionUtil.callMethod(gl, finalizeInit, new Object[]{ });
}
}
public final ProcAddressTable getGLProcAddressTable() {
return glProcAddressTable;
}
/**
* Shall return the platform extension ProcAddressTable,
* ie for GLXExt, EGLExt, ..
*/
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(String glFunctionName) {
Map map = getFunctionNameMap();
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(String glExtensionName) {
Map map = getExtensionNameMap();
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