/** * Copyright (c) 2010-2023 JogAmp Community. All rights reserved. * Copyright (c) 2003 Sun Microsystems, Inc. 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. */ 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.Clock; 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.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.GLExtensions; import com.jogamp.opengl.GLPipelineFactory; import com.jogamp.opengl.GLProfile; import com.jogamp.opengl.GLRendererQuirks; public abstract class GLContextImpl extends GLContext { /** Selected {@link Platform.OSType#MACOS} or {@link Platform.OSType#IOS} {@link VersionNumber}s. */ public static class MacOSVersion { /** OSX Lion, i.e. 10.7.0 */ public static final VersionNumber Lion; /** OSX Mavericks, i.e. 10.9.0 */ public static final VersionNumber Mavericks; /** OSX High Sierra, i.e. 10.13.0 */ public static final VersionNumber HighSierra; /** OSX Mojave, i.e. 10.14.0 */ public static final VersionNumber Mojave; static { if( Platform.getOSType() == Platform.OSType.MACOS ) { Lion = new VersionNumber(10,7,0); Mavericks = new VersionNumber(10,9,0); HighSierra = new VersionNumber(10,13,0); Mojave = new VersionNumber(10,14,0); } else { Lion = null; Mavericks = null; HighSierra = null; Mojave = null; } } } /** * 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; /** * */ 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: res "+lockRes+", "+drawable+", "+toString()); } 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:
*
    *
  • {@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 { 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 ) ? Clock.currentNanos() : 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(MacOSVersion.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 = Clock.currentNanos(); 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() { @Override public Object run() { table.reset( dlh ); return null; } } ); } /** * Updates the platform's 'GLX' function cache * @param contextFQN provides a fully qualified key of the context including device and GL profile * @param dlh {@link GLDynamicLookupHelper} used to {@link #resetProcAddressTable(ProcAddressTable, GLDynamicLookupHelper)} instance. */ protected abstract void updateGLXProcAddressTable(final String contextFQN, final GLDynamicLookupHelper dlh); private final boolean initGLRendererAndGLVersionStrings(final int majorVersion, final int contextOptions) { final String _glVendor = glGetStringInt(GL.GL_VENDOR); if(null == _glVendor) { if(DEBUG) { System.err.println("Warning: GL_VENDOR is NULL."); ExceptionUtils.dumpStack(System.err); } return false; } glVendor = _glVendor; final String _glRenderer = glGetStringInt(GL.GL_RENDERER); if(null == _glRenderer) { if(DEBUG) { System.err.println("Warning: GL_RENDERER is NULL."); ExceptionUtils.dumpStack(System.err); } return false; } glRenderer = _glRenderer; glRendererLowerCase = glRenderer.toLowerCase(); final String _glVersion = glGetStringInt(GL.GL_VERSION); if(null == _glVersion) { // FIXME if(DEBUG) { System.err.println("Warning: GL_VERSION is NULL."); ExceptionUtils.dumpStack(System.err); } return false; } glVersion = _glVersion; return true; } /** * Returns false if glGetIntegerv is inaccessible, otherwise queries major.minor * version for given arrays. *

* If the GL query fails, major will be zero. *

*/ private final void getGLIntVersion(final int[] glIntMajor, final int[] glIntMinor, final int[] glIntCtxProfileMask) { glIntMajor[0] = 0; // clear glIntMinor[0] = 0; // clear glGetIntegervInt(GL2ES3.GL_MAJOR_VERSION, glIntMajor, 0); glGetIntegervInt(GL2ES3.GL_MINOR_VERSION, glIntMinor, 0); if( null != glIntCtxProfileMask ) { glGetIntegervInt(GL3.GL_CONTEXT_PROFILE_MASK, glIntCtxProfileMask, 0); } } /** * Returns null if version string is invalid, otherwise a valid instance. *

* Note: Non ARB ctx is limited to GL 3.0. *

*/ private static final VersionNumber getGLVersionNumber(final int ctp, final String glVersionStr) { if( null != glVersionStr ) { final GLVersionNumber version = GLVersionNumber.create(glVersionStr); if ( version.isValid() ) { final int[] major = new int[] { version.getMajor() }; final int[] minor = new int[] { version.getMinor() }; if ( GLContext.isValidGLVersion(ctp, major[0], minor[0]) ) { return new VersionNumber(major[0], minor[0], 0); } } } return null; } protected final int getCtxOptions() { return ctxOptions; } /** * Sets the OpenGL implementation class and * the cache of which GL functions are available for calling through this * context. See {@link #isFunctionAvailable(String)} for more information on * the definition of "available". *

* All ProcaddressTables are being determined and cached, the GL version is being set * and the extension cache is determined as well. *

*

* It is the callers responsibility to issue {@link #resetStates(boolean)} * in case this method returns {@code false} or throws a {@link GLException}. *

* * @param force force the setting, even if is already being set. * This might be useful if you change the OpenGL implementation. * @param reqMajor requested OpenGL major version * @param reqMinor requested OpenGL minor version * @param reqCtxProfileBits requested OpenGL context profile and option bits, see {@link com.jogamp.opengl.GLContext#CTX_OPTION_ANY} * @param strictMatch if true the ctx must *
    *
  • be greater or equal than the requested reqMajor.reqMinor version, and
  • *
  • match the reqCtxProfileBits
  • *
  • match ES reqMajor versions
  • *
, otherwise method aborts and returns false.
* if false no version check is performed. * @param withinGLVersionsMapping if true GL version mapping is in process, i.e. querying avail versions. * Otherwise normal user context creation. * @return returns true if successful, otherwise false.
* If strictMatch is false method shall always return true or throw an exception. * If false is returned, no data has been cached or mapped, i.e. ProcAddressTable, Extensions, Version, etc. * @throws GLException in case of an unexpected OpenGL related issue, e.g. missing expected GL function pointer. * @see #setContextVersion * @see com.jogamp.opengl.GLContext#CTX_OPTION_ANY * @see com.jogamp.opengl.GLContext#CTX_PROFILE_COMPAT * @see com.jogamp.opengl.GLContext#CTX_PROFILE_CORE * @see com.jogamp.opengl.GLContext#CTX_PROFILE_ES * @see com.jogamp.opengl.GLContext#CTX_IMPL_ES2_COMPAT */ protected final boolean setGLFunctionAvailability(final boolean force, final int reqMajor, final int reqMinor, final int reqCtxProfileBits, final boolean strictMatch, final boolean withinGLVersionsMapping) throws GLException { if( null != this.gl && null != glProcAddressTable && !force ) { return true; // already done and not forced } if ( 1 < Bitfield.Util.bitCount( reqCtxProfileBits & ( CTX_PROFILE_ES | CTX_PROFILE_CORE | CTX_PROFILE_COMPAT ) ) ) { final String reqCtxProfileString = getGLProfile(new StringBuilder(), reqCtxProfileBits).toString(); final int val = reqCtxProfileBits & ( CTX_PROFILE_ES | CTX_PROFILE_CORE | CTX_PROFILE_COMPAT ); throw new GLException("Invalid GL Profile Request, only one can be set of ES, CORE and COMPAT. Has <"+reqCtxProfileString+ ">, bits <"+Integer.toBinaryString(val)+">, count "+Bitfield.Util.bitCount( val ) ); } if ( 0 < reqMajor && !GLContext.isValidGLVersion(reqCtxProfileBits, reqMajor, reqMinor) ) { throw new GLException("Invalid GL Version Request "+GLContext.getGLVersion(reqMajor, reqMinor, reqCtxProfileBits, null)); } final AbstractGraphicsConfiguration aconfig = drawable.getNativeSurface().getGraphicsConfiguration(); final AbstractGraphicsDevice adevice = aconfig.getScreen().getDevice(); if( !glGetPtrInit ) { SecurityUtil.doPrivileged(new PrivilegedAction() { @Override public Object run() { final GLDynamicLookupHelper glDynLookupHelper = getGLDynamicLookupHelper(reqMajor, reqCtxProfileBits); if( null != glDynLookupHelper ) { glDynLookupHelper.claimAllLinkPermission(); try { glGetStringPtr = glDynLookupHelper.dynamicLookupFunction("glGetString"); glGetIntegervPtr = glDynLookupHelper.dynamicLookupFunction("glGetIntegerv"); glGetStringiPtr = glDynLookupHelper.dynamicLookupFunction("glGetStringi"); // optional } finally { glDynLookupHelper.releaseAllLinkPermission(); } } return null; } } ); glGetPtrInit = true; if(DEBUG) { System.err.println(getThreadName() + ": GLContext.setGLFuncAvail: glGetStringi "+toHexString(glGetStringiPtr)+" (opt), glGetString "+toHexString(glGetStringPtr)+", glGetIntegerv "+toHexString(glGetIntegervPtr)); } if( 0 == glGetStringPtr || 0 == glGetIntegervPtr ) { final String errMsg = "Intialization of glGetString "+toHexString(glGetStringPtr)+", glGetIntegerv "+toHexString(glGetIntegervPtr)+" failed. "+adevice+" - requested "+ GLContext.getGLVersion(reqMajor, reqMinor, reqCtxProfileBits, null); if( strictMatch ) { // query mode .. simply fail if(DEBUG) { System.err.println("Warning: setGLFunctionAvailability: "+errMsg); } return false; } else { // unusable GL context - non query mode - hard fail! throw new GLException(errMsg); } } } final VersionNumber hasGLVersionByString; { final boolean initGLRendererAndGLVersionStringsOK = initGLRendererAndGLVersionStrings(reqMajor, reqCtxProfileBits); if( !initGLRendererAndGLVersionStringsOK ) { final String errMsg = "Intialization of GL renderer strings failed. "+adevice+" - requested "+ GLContext.getGLVersion(reqMajor, reqMinor, reqCtxProfileBits, null); if( strictMatch ) { // query mode .. simply fail if(DEBUG) { System.err.println("Warning: setGLFunctionAvailability: "+errMsg); } return false; } else { // unusable GL context - non query mode - hard fail! throw new GLException(errMsg); } } else { hasGLVersionByString = getGLVersionNumber(reqCtxProfileBits, glVersion); if(DEBUG) { System.err.println(getThreadName() + ": GLContext.setGLFuncAvail: Given "+adevice+ " - requested "+GLContext.getGLVersion(reqMajor, reqMinor, reqCtxProfileBits, glVersion)+ ", has Number(Str) "+hasGLVersionByString); } } } final boolean isESReq = 0 != ( CTX_PROFILE_ES & reqCtxProfileBits ); // // Validate GL version either by GL-Integer or GL-String // if (DEBUG) { System.err.println(getThreadName() + ": GLContext.setGLFuncAvail: Pre version verification: requested "+ GLContext.getGLVersion(reqMajor, reqMinor, reqCtxProfileBits, null)+ ", drawable.glp "+drawable.getGLProfile()+ ", strictMatch "+strictMatch+", glVersionsMapping " +withinGLVersionsMapping+ ", hasGLVersionByString "+hasGLVersionByString); } final VersionNumber reqGLVersion = new VersionNumber(reqMajor, reqMinor, 0); int hasMajor = reqMajor; int hasMinor = reqMinor; int hasCtxProfileBits = reqCtxProfileBits; final boolean versionGL3IntOK; { // Validate the requested version w/ the GL-version from an integer query, // as supported by GL [ES] >= 3.0 implementation. // // Only validate integer based version if: // - ctx >= 3.0 is requested _or_ string-version >= 3.0 // - _and_ a valid int version was fetched, // otherwise cont. w/ version-string method -> 3.0 > Version || Version > MAX! // final VersionNumber hasGLVersionByInt; if ( reqMajor >= 3 || hasGLVersionByString.compareTo(Version3_0) >= 0 ) { final int[] glIntMajor = new int[] { 0 }, glIntMinor = new int[] { 0 }; if( isESReq ) { getGLIntVersion(glIntMajor, glIntMinor, null); } else { final int[] glIntCtxProfileMask = new int[] { 0 }; getGLIntVersion(glIntMajor, glIntMinor, glIntCtxProfileMask); if( 0 != ( GL3.GL_CONTEXT_CORE_PROFILE_BIT & glIntCtxProfileMask[0] ) ) { hasCtxProfileBits &= ~GLContext.CTX_PROFILE_COMPAT; hasCtxProfileBits |= GLContext.CTX_PROFILE_CORE; } else if( 0 != ( GL3.GL_CONTEXT_COMPATIBILITY_PROFILE_BIT & glIntCtxProfileMask[0] ) ) { hasCtxProfileBits |= GLContext.CTX_PROFILE_COMPAT; hasCtxProfileBits &= ~GLContext.CTX_PROFILE_CORE; } } if ( 0 == Bitfield.Util.bitCount( hasCtxProfileBits & ( CTX_PROFILE_ES | CTX_PROFILE_CORE | CTX_PROFILE_COMPAT ) ) ) { // set default GL profile if non has been given nor queried from the GL state hasCtxProfileBits |= isESReq ? GLContext.CTX_PROFILE_ES : GLContext.CTX_PROFILE_COMPAT; } hasGLVersionByInt = new VersionNumber(glIntMajor[0], glIntMinor[0], 0); if (DEBUG) { System.err.println(getThreadName() + ": GLContext.setGLFuncAvail: Version verification (Int): String "+glVersion+", Number(Int) "+ hasGLVersionByInt+" - "+GLContext.getGLVersion(glIntMajor[0], glIntMinor[0], hasCtxProfileBits, null)); } if ( GLContext.isValidGLVersion(hasCtxProfileBits, hasGLVersionByInt.getMajor(), hasGLVersionByInt.getMinor()) ) { // Strict Match (GLVersionMapping): // Relaxed match for versions ( !isES && major < 3 ) requests, last resort! // Otherwise: // - fail if hasVersion < reqVersion (desktop and ES) // - fail if requested compat && has core profile (desktop) // - fail if ES major-version mismatch: // - request 1, >= 3 must be equal // - request 2 must be [2..3] // final int _hasMajor = hasGLVersionByInt.getMajor(); if( strictMatch && ( ( ( isESReq || reqMajor >= 3 ) && hasGLVersionByInt.compareTo(reqGLVersion) < 0 ) || ( !isESReq && 0 != ( reqCtxProfileBits & GLContext.CTX_PROFILE_COMPAT ) && 0 != ( hasCtxProfileBits & GLContext.CTX_PROFILE_CORE ) ) || ( isESReq && ( ( 2 == reqMajor && ( 2 > _hasMajor || _hasMajor > 3 ) ) || // 2 -> [2..3] ( ( 1 == reqMajor || 3 <= reqMajor ) && reqMajor != _hasMajor ) // 1,3,.. -> equal ) ) ) ) { if(DEBUG) { System.err.println(getThreadName() + ": GLContext.setGLFuncAvail.X: FAIL, GL version mismatch (Int): requested "+ GLContext.getGLVersion(reqMajor, reqMinor, reqCtxProfileBits, null)+ " -> has "+glVersion+", "+hasGLVersionByInt+" - "+ GLContext.getGLVersion(glIntMajor[0], glIntMinor[0], hasCtxProfileBits, null)); } return false; } // Use returned GL version! hasMajor = hasGLVersionByInt.getMajor(); hasMinor = hasGLVersionByInt.getMinor(); versionGL3IntOK = true; } else { versionGL3IntOK = false; } } else { versionGL3IntOK = false; } } final boolean versionValidated; if( versionGL3IntOK ) { versionValidated = true; } else { // Validate the requested version w/ the GL-version from the version string. if (DEBUG) { System.err.println(getThreadName() + ": GLContext.setGLFuncAvail: Version verification (String): String "+ glVersion+", Number(Str) "+hasGLVersionByString); } if ( 0 == Bitfield.Util.bitCount( hasCtxProfileBits & ( CTX_PROFILE_ES | CTX_PROFILE_CORE | CTX_PROFILE_COMPAT ) ) ) { // set default GL profile if non has been given nor queried from the GL state hasCtxProfileBits |= isESReq ? GLContext.CTX_PROFILE_ES : GLContext.CTX_PROFILE_COMPAT; } // Only validate if a valid string version was fetched -> MIN > Version || Version > MAX! if( null != hasGLVersionByString ) { // Strict Match (GLVersionMapping): // Relaxed match for versions ( !isES && major < 3 ) requests, last resort! // Otherwise: // - fail if hasVersion < reqVersion (desktop and ES) // - fail if requested compat && has core profile (desktop) // - fail if ES major-version mismatch: // - request 1, >= 3 must be equal // - request 2 must be [2..3] // final int _hasMajor = hasGLVersionByString.getMajor(); if( strictMatch && ( ( ( isESReq || reqMajor >= 3 ) && hasGLVersionByString.compareTo(reqGLVersion) < 0 ) || ( !isESReq && 0 != ( reqCtxProfileBits & GLContext.CTX_PROFILE_COMPAT ) && 0 != ( hasCtxProfileBits & GLContext.CTX_PROFILE_CORE ) ) || ( isESReq && ( ( 2 == reqMajor && ( 2 > _hasMajor || _hasMajor > 3 ) ) || // 2 -> [2..3] ( ( 1 == reqMajor || 3 <= reqMajor ) && reqMajor != _hasMajor ) // 1,3,.. -> equal ) ) ) ) { if(DEBUG) { System.err.println(getThreadName() + ": GLContext.setGLFuncAvail.X: FAIL, GL version mismatch (String): requested "+ GLContext.getGLVersion(reqMajor, reqMinor, reqCtxProfileBits, null)+ " -> has "+glVersion+", "+hasGLVersionByString+" - "+ GLContext.getGLVersion(hasGLVersionByString.getMajor(), hasGLVersionByString.getMinor(), hasCtxProfileBits, null)); } return false; } if( strictMatch && !versionGL3IntOK && reqMajor >= 3 ) { if(DEBUG) { System.err.println(getThreadName() + ": GLContext.setGLFuncAvail.X: FAIL, GL3/ES3 version Int failed, String: requested "+ GLContext.getGLVersion(reqMajor, reqMinor, reqCtxProfileBits, null)+" -> "+glVersion+", "+hasGLVersionByString); } return false; } // Use returned GL version! hasMajor = hasGLVersionByString.getMajor(); hasMinor = hasGLVersionByString.getMinor(); versionValidated = true; } else { versionValidated = false; } } if( strictMatch && !versionValidated ) { if(DEBUG) { System.err.println(getThreadName() + ": GLContext.setGLFuncAvail.X: FAIL, No GL version validation possible: requested "+ GLContext.getGLVersion(reqMajor, reqMinor, reqCtxProfileBits, null)+" -> has "+ GLContext.getGLVersion(hasMajor, hasMinor, hasCtxProfileBits, null)+" - "+glVersion); } return false; } if (DEBUG) { System.err.println(getThreadName() + ": GLContext.setGLFuncAvail: Post version verification: requested "+ GLContext.getGLVersion(reqMajor, reqMinor, reqCtxProfileBits, null)+" -> has "+ GLContext.getGLVersion(hasMajor, hasMinor, hasCtxProfileBits, null)+ ", strictMatch "+strictMatch+", versionValidated "+versionValidated+", versionGL3IntOK "+versionGL3IntOK); } if( hasMajor < 2 ) { // there is no ES2/3-compat for a profile w/ major < 2 hasCtxProfileBits &= ~ ( GLContext.CTX_IMPL_ES2_COMPAT | GLContext.CTX_IMPL_ES3_COMPAT | GLContext.CTX_IMPL_ES31_COMPAT | GLContext.CTX_IMPL_ES32_COMPAT ) ; } if(!isCurrentContextHardwareRasterizer()) { hasCtxProfileBits |= GLContext.CTX_IMPL_ACCEL_SOFT; } final VersionNumberString vendorVersion = GLVersionNumber.createVendorVersion(glVersion); setRendererQuirks(adevice, getDrawableImpl().getFactoryImpl(), reqMajor, reqMinor, reqCtxProfileBits, hasMajor, hasMinor, hasCtxProfileBits, vendorVersion, withinGLVersionsMapping); if( glRendererQuirks.exist(GLRendererQuirks.GL3CompatNonCompliant) && 0 != ( reqCtxProfileBits & GLContext.CTX_PROFILE_COMPAT) && (reqMajor > 3 || (reqMajor == 3 && reqMinor >= 1)) ) { if(DEBUG) { System.err.println(getThreadName() + ": GLContext.setGLFuncAvail.X: FAIL, GL3CompatNonCompliant: "+ GLContext.getGLVersion(hasMajor, hasMinor, hasCtxProfileBits, glVersion)+", "+glRenderer); } return false; } if( glRendererQuirks.exist(GLRendererQuirks.GL3CompatNonCompliant) && reqMajor > 0 && 0 != ( hasCtxProfileBits & GLContext.CTX_PROFILE_COMPAT) && (hasMajor > 3 || (hasMajor == 3 && hasMinor >= 1)) ) { // Clip actual OpenGL version to be mapped for requested profile/version mapping hasMajor = reqMajor; hasMinor = reqMinor; if(DEBUG) { System.err.println(getThreadName() + ": GLContext.setGLFuncAvail: GL3CompatNonCompliant: "+ GLContext.getGLVersion(hasMajor, hasMinor, hasCtxProfileBits, glVersion)+", "+glRenderer); } } contextFQN = getContextFQN(adevice, hasMajor, hasMinor, hasCtxProfileBits); if (DEBUG) { System.err.println(getThreadName() + ": GLContext.setGLFuncAvail.0 validated FQN: "+contextFQN+" - "+ GLContext.getGLVersion(hasMajor, hasMinor, hasCtxProfileBits, glVersion)); } final GLDynamicLookupHelper dynamicLookup = getGLDynamicLookupHelper(hasMajor, hasCtxProfileBits); if( null == dynamicLookup ) { if(DEBUG) { System.err.println(getThreadName() + ": GLContext.setGLFuncAvail.X: FAIL, No GLDynamicLookupHelper for having: "+ GLContext.getGLVersion(hasMajor, hasMinor, hasCtxProfileBits, null)); } return false; } updateGLXProcAddressTable(contextFQN, dynamicLookup); // // UpdateGLProcAddressTable functionality // _and_ setup GL instance, which ctor requires valid getGLProcAddressTable() result! // { final GLProfile glp = drawable.getGLProfile(); // !withinGLVersionsMapping ProcAddressTable table = null; synchronized(mappedContextTypeObjectLock) { table = mappedGLProcAddress.get( contextFQN ); if(null != table) { if( !verifyInstance(adevice, hasMajor, hasMinor, hasCtxProfileBits, "ProcAddressTable", table) ) { throw new GLException("GLContext GL ProcAddressTable mapped key("+contextFQN+" - " + GLContext.getGLVersion(hasMajor, hasMinor, hasCtxProfileBits, null)+ ") -> "+ toHexString(table.hashCode()) +" not matching "+table.getClass().getName()); } if( !withinGLVersionsMapping && !verifyInstance(glp, "ProcAddressTable", table) ) { throw new GLException("GLContext GL ProcAddressTable mapped key("+contextFQN+" - " + GLContext.getGLVersion(hasMajor, hasMinor, hasCtxProfileBits, null)+ ") -> "+ toHexString(table.hashCode()) +": "+table.getClass().getName()+ " not matching "+glp.getGLImplBaseClassName()+"/"+glp); } } } if(null != table) { glProcAddressTable = table; if(DEBUG) { if( withinGLVersionsMapping ) { System.err.println(getThreadName() + ": GLContext GL ProcAddressTable reusing key("+contextFQN+" - " + GLContext.getGLVersion(hasMajor, hasMinor, hasCtxProfileBits, null)+ ") -> "+ toHexString(table.hashCode()) +": "+table.getClass().getName()); } else { System.err.println(getThreadName() + ": GLContext GL ProcAddressTable reusing key("+contextFQN+" - " + GLContext.getGLVersion(hasMajor, hasMinor, hasCtxProfileBits, null)+ ") -> "+ toHexString(table.hashCode()) +": "+table.getClass().getName()+" -> "+glp.getGLImplBaseClassName()); } } } else { glProcAddressTable = (ProcAddressTable) createInstance(adevice, hasMajor, hasMinor, hasCtxProfileBits, false, new Object[] { new GLProcAddressResolver() } ); resetProcAddressTable(glProcAddressTable, dynamicLookup); synchronized(mappedContextTypeObjectLock) { mappedGLProcAddress.put(contextFQN, glProcAddressTable); if(DEBUG) { if( withinGLVersionsMapping ) { System.err.println(getThreadName() + ": GLContext GL ProcAddressTable mapping key("+contextFQN+" - " + GLContext.getGLVersion(hasMajor, hasMinor, hasCtxProfileBits, null)+ ") -> "+toHexString(glProcAddressTable.hashCode()) +": "+glProcAddressTable.getClass().getName()); } else { System.err.println(getThreadName() + ": GLContext GL ProcAddressTable mapping key("+contextFQN+" - " + GLContext.getGLVersion(hasMajor, hasMinor, hasCtxProfileBits, null)+ ") -> "+toHexString(glProcAddressTable.hashCode()) +": "+glProcAddressTable.getClass().getName()+ " -> "+glp.getGLImplBaseClassName()); } } } } if( null == this.gl || !verifyInstance(adevice, hasMajor, hasMinor, hasCtxProfileBits, "Impl", this.gl) ) { setGL( createGL( adevice, hasMajor, hasMinor, hasCtxProfileBits ) ); } if( !withinGLVersionsMapping && !verifyInstance(glp, "Impl", this.gl) ) { throw new GLException("GLContext GL Object mismatch: "+GLContext.getGLVersion(hasMajor, hasMinor, hasCtxProfileBits, null)+ ") -> "+": "+this.gl.getClass().getName()+" not matching "+glp.getGLImplBaseClassName()+"/"+glp); } } // // Update ExtensionAvailabilityCache // { ExtensionAvailabilityCache eCache; synchronized(mappedContextTypeObjectLock) { eCache = mappedExtensionAvailabilityCache.get( contextFQN ); } if(null != eCache) { extensionAvailability = eCache; if(DEBUG) { System.err.println(getThreadName() + ": GLContext GL ExtensionAvailabilityCache reusing key("+contextFQN+") -> "+ toHexString(eCache.hashCode()) + " - entries: "+eCache.getTotalExtensionCount()); } } else { extensionAvailability = new ExtensionAvailabilityCache(); setContextVersion(hasMajor, hasMinor, hasCtxProfileBits, vendorVersion, false); // pre-set of GL version, required for extension cache usage extensionAvailability.reset(this); synchronized(mappedContextTypeObjectLock) { mappedExtensionAvailabilityCache.put(contextFQN, extensionAvailability); if(DEBUG) { System.err.println(getThreadName() + ": GLContext GL ExtensionAvailabilityCache mapping key("+contextFQN+") -> "+ toHexString(extensionAvailability.hashCode()) + " - entries: "+extensionAvailability.getTotalExtensionCount()); } } } } if( isESReq ) { if( hasMajor >= 3 ) { hasCtxProfileBits |= CTX_IMPL_ES3_COMPAT | CTX_IMPL_ES2_COMPAT ; hasCtxProfileBits |= CTX_IMPL_FBO; if( hasMinor >= 2 ) { hasCtxProfileBits |= CTX_IMPL_ES32_COMPAT | CTX_IMPL_ES31_COMPAT; } else if( hasMinor >= 1 ) { hasCtxProfileBits |= CTX_IMPL_ES31_COMPAT; } } else if( hasMajor >= 2 ) { hasCtxProfileBits |= CTX_IMPL_ES2_COMPAT; hasCtxProfileBits |= CTX_IMPL_FBO; } } else if( ( hasMajor > 4 || hasMajor == 4 && hasMinor >= 5 ) || ( hasMajor > 3 || hasMajor == 3 && hasMinor >= 1 ) ) { // See GLContext.isGLES31CompatibleAvailable(..)/isGLES3[12]Compatible() // Includes [ GL ≥ 4.5, GL ≥ 3.1 w/ GL_ARB_ES3_[12]_compatibility and GLES ≥ 3.[12] ] if( isExtensionAvailable( GLExtensions.ARB_ES3_2_compatibility ) ) { hasCtxProfileBits |= CTX_IMPL_ES32_COMPAT | CTX_IMPL_ES31_COMPAT; } else if( isExtensionAvailable( GLExtensions.ARB_ES3_1_compatibility ) ) { hasCtxProfileBits |= CTX_IMPL_ES31_COMPAT; } hasCtxProfileBits |= CTX_IMPL_ES3_COMPAT | CTX_IMPL_ES2_COMPAT; hasCtxProfileBits |= CTX_IMPL_FBO; } else if( ( hasMajor > 4 || hasMajor == 4 && hasMinor >= 3 ) || ( ( hasMajor > 3 || hasMajor == 3 && hasMinor >= 1 ) && isExtensionAvailable( GLExtensions.ARB_ES3_compatibility ) ) ) { // See GLContext.isGLES3CompatibleAvailable(..)/isGLES3Compatible() // Includes [ GL ≥ 4.3, GL ≥ 3.1 w/ GL_ARB_ES3_compatibility and GLES3 ] hasCtxProfileBits |= CTX_IMPL_ES3_COMPAT | CTX_IMPL_ES2_COMPAT ; hasCtxProfileBits |= CTX_IMPL_FBO; } else if( isExtensionAvailable( GLExtensions.ARB_ES2_compatibility ) ) { hasCtxProfileBits |= CTX_IMPL_ES2_COMPAT; hasCtxProfileBits |= CTX_IMPL_FBO; } else if( hasFBOImpl(hasMajor, hasCtxProfileBits, extensionAvailability) ) { hasCtxProfileBits |= CTX_IMPL_FBO; } if( ( isESReq && hasMajor == 1 ) || isExtensionAvailable(GLExtensions.OES_single_precision) ) { hasCtxProfileBits |= CTX_IMPL_FP32_COMPAT_API; } if( glRendererQuirks.exist(GLRendererQuirks.NoFBOSupport) ) { hasCtxProfileBits &= ~CTX_IMPL_FBO ; } // // Set GL Version (complete w/ version string) // setContextVersion(hasMajor, hasMinor, hasCtxProfileBits, vendorVersion, true); finalizeInit(gl); setDefaultSwapInterval(); final int glErrX = gl.glGetError(); // clear GL error, maybe caused by above operations if(DEBUG) { System.err.println(getThreadName() + ": GLContext.setGLFuncAvail.X: OK "+contextFQN+" - "+ GLContext.getGLVersion(ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions, null)+" - glErr "+toHexString(glErrX)); } return true; } private static final void addStickyQuirkAlways(final AbstractGraphicsDevice adevice, final GLRendererQuirks quirks, final int quirk, final boolean withinGLVersionsMapping) { quirks.addQuirk( quirk ); if( withinGLVersionsMapping ) { // Thread safe due to single threaded initialization! GLRendererQuirks.addStickyDeviceQuirk(adevice, quirk); } else { // FIXME: Remove when moving EGL/ES to ARB ctx creation synchronized(GLContextImpl.class) { GLRendererQuirks.addStickyDeviceQuirk(adevice, quirk); } } } private static final void addStickyQuirkAtMapping(final AbstractGraphicsDevice adevice, final GLRendererQuirks quirks, final int quirk, final boolean withinGLVersionsMapping) { quirks.addQuirk( quirk ); if( withinGLVersionsMapping ) { // Thread safe due to single threaded initialization! GLRendererQuirks.addStickyDeviceQuirk(adevice, quirk); } } private final void setRendererQuirks(final AbstractGraphicsDevice adevice, final GLDrawableFactoryImpl factory, final int reqMajor, final int reqMinor, final int reqCTP, final int hasMajor, final int hasMinor, final int hasCTP, final VersionNumberString vendorVersion, final boolean withinGLVersionsMapping) { final String MesaSP = "Mesa "; // final String MesaRendererAMDsp = " AMD "; final String MesaRendererIntelsp = "Intel(R)"; final boolean hwAccel = 0 == ( hasCTP & GLContext.CTX_IMPL_ACCEL_SOFT ); final boolean compatCtx = 0 != ( hasCTP & GLContext.CTX_PROFILE_COMPAT ); final boolean isES = 0 != ( hasCTP & GLContext.CTX_PROFILE_ES ); final boolean isX11 = NativeWindowFactory.TYPE_X11 == NativeWindowFactory.getNativeWindowType(true); final boolean isWindows = Platform.getOSType() == Platform.OSType.WINDOWS; final boolean isDriverMesa = glRenderer.contains(MesaSP) || glRenderer.contains("Gallium ") || glVersion.contains(MesaSP); final boolean isDriverATICatalyst; final boolean isDriverNVIDIAGeForce; final boolean isDriverIntel; if( !isDriverMesa ) { isDriverATICatalyst = glVendor.contains("ATI Technologies") || glRenderer.startsWith("ATI "); isDriverNVIDIAGeForce = glVendor.contains("NVIDIA Corporation") || glRenderer.contains("NVIDIA "); isDriverIntel = glVendor.startsWith("Intel"); } else { isDriverATICatalyst = false; isDriverNVIDIAGeForce = false; isDriverIntel = false; } final GLRendererQuirks quirks = new GLRendererQuirks(); // // General Quirks // if( isES ) { if( 2 == reqMajor && 2 < hasMajor ) { final int quirk = GLRendererQuirks.GLES3ViaEGLES2Config; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: ES req "+reqMajor+" and has 2 < "+hasMajor); } addStickyQuirkAlways(adevice, quirks, quirk, withinGLVersionsMapping); } } if( GLProfile.disableSurfacelessContext ) { final int quirk = GLRendererQuirks.NoSurfacelessCtx; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: disabled"); } addStickyQuirkAlways(adevice, quirks, quirk, withinGLVersionsMapping); } if( GLProfile.disableOpenGLARBContext ) { final int quirk = GLRendererQuirks.NoARBCreateContext; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: disabled"); } addStickyQuirkAlways(adevice, quirks, quirk, withinGLVersionsMapping); } // // OS related quirks // if( Platform.getOSType() == Platform.OSType.MACOS ) { // // OSX // { final int quirk = GLRendererQuirks.NoOffscreenBitmap; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()); } quirks.addQuirk( quirk ); } { final int quirk = GLRendererQuirks.NeedSharedObjectSync; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()); } quirks.addQuirk( quirk ); } if( Platform.getOSVersionNumber().compareTo(MacOSVersion.Mavericks) >= 0 && 3==reqMajor && 4==hasMajor ) { final int quirk = GLRendererQuirks.GL4NeedsGL3Request; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()+", OS Version "+Platform.getOSVersionNumber()+", req "+reqMajor+"."+reqMinor); } addStickyQuirkAtMapping(adevice, quirks, quirk, withinGLVersionsMapping); } if( isDriverNVIDIAGeForce ) { final VersionNumber osxVersionNVFlushClean = new VersionNumber(10,7,3); // < OSX 10.7.3 w/ NV needs glFlush if( Platform.getOSVersionNumber().compareTo(osxVersionNVFlushClean) < 0 ) { final int quirk = GLRendererQuirks.GLFlushBeforeRelease; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()+", OS Version "+Platform.getOSVersionNumber()+", Renderer "+glRenderer); } quirks.addQuirk( quirk ); } if( Platform.getOSVersionNumber().compareTo(MacOSVersion.Lion) < 0 ) { // < OSX 10.7.0 w/ NV has unstable GLSL final int quirk = GLRendererQuirks.GLSLNonCompliant; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()+", OS Version "+Platform.getOSVersionNumber()+", Renderer "+glRenderer); } quirks.addQuirk( quirk ); } } } else if( isWindows ) { // // WINDOWS // { final int quirk = GLRendererQuirks.NoDoubleBufferedBitmap; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()); } quirks.addQuirk( quirk ); } if( isDriverATICatalyst ) { final VersionNumber winXPVersionNumber = new VersionNumber ( 5, 1, 0); final VersionNumber amdSafeMobilityVersion = new VersionNumber(12, 102, 3); if ( vendorVersion.compareTo(amdSafeMobilityVersion) < 0 ) { // includes: vendorVersion.isZero() final int quirk = GLRendererQuirks.NeedCurrCtx4ARBCreateContext; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()+", [Vendor "+glVendor+" or Renderer "+glRenderer+"], driverVersion "+vendorVersion); } quirks.addQuirk( quirk ); } if( Platform.getOSVersionNumber().compareTo(winXPVersionNumber) <= 0 ) { final int quirk = GLRendererQuirks.NeedCurrCtx4ARBPixFmtQueries; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS-Version "+Platform.getOSType()+" "+Platform.getOSVersionNumber()+", [Vendor "+glVendor+" or Renderer "+glRenderer+"]"); } quirks.addQuirk( quirk ); } if ( vendorVersion.compareTo(VersionNumberString.zeroVersion) == 0 ) { final VersionNumber glVersionNumber = new VersionNumber(glVersion); if ( glVersionNumber.getSub() <= 8787 && glRenderer.equals("ATI Radeon 3100 Graphics") ) { // "old" driver -> sub-minor = vendor version final int quirk = GLRendererQuirks.NoARBCreateContext; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()+", [Vendor "+glVendor+", Renderer "+glRenderer+" and Version "+glVersion+"]"); } addStickyQuirkAtMapping(adevice, quirks, quirk, withinGLVersionsMapping); } } } else if( isDriverIntel && glRenderer.equals("Intel Bear Lake B") ) { final int quirk = GLRendererQuirks.NoPBufferWithAccum; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()+", [Vendor "+glVendor+" and Renderer "+glRenderer+"]"); } quirks.addQuirk( quirk ); } } else if( Platform.OSType.ANDROID == Platform.getOSType() ) { // // ANDROID // // Renderer related quirks, may also involve OS if( glRenderer.contains("PowerVR") ) { final int quirk = GLRendererQuirks.NoSetSwapInterval; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType() + ", Renderer " + glRenderer); } quirks.addQuirk( quirk ); } if( glRenderer.contains("Immersion.16") ) { final int quirk = GLRendererQuirks.GLSharedContextBuggy; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType() + ", Renderer " + glRenderer); } quirks.addQuirk( quirk ); } } // // Windowing Toolkit related quirks // if( isX11 ) { // // X11 // { // // Quirk: DontCloseX11Display // final int quirk = GLRendererQuirks.DontCloseX11Display; if( glRenderer.contains(MesaSP) ) { if ( glRenderer.contains("X11") && vendorVersion.compareTo(Version8_0) < 0 ) { if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: X11 Renderer=" + glRenderer + ", Version=[vendor " + vendorVersion + ", GL " + glVersion+"]"); } quirks.addQuirk( quirk ); } } else if( isDriverATICatalyst ) { { if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: X11 Renderer=" + glRenderer); } quirks.addQuirk( quirk ); } } else if( jogamp.nativewindow.x11.X11Util.getMarkAllDisplaysUnclosable() ) { { if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: X11Util Downstream"); } quirks.addQuirk( quirk ); } } } if( isDriverNVIDIAGeForce ) { // Bug 1200: Crash on GNU/Linux x86_64 'NVidia beta driver 355.06' and 'latest' 440.36 @ probeSurfacelessCtx // final VersionNumber nvSafeVersion = new VersionNumber(440, 36, 0); // FIXME: Add safe version if( !isES && !(adevice instanceof EGLGraphicsDevice) /* && vendorVersion.compareTo(nvSafeVersion) < 0 */ ) { final int quirk = GLRendererQuirks.NoSurfacelessCtx; if(DEBUG) { System.err.print("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: !ES, !EGL, Vendor " + glVendor +", X11 Renderer " + glRenderer+", Version=[vendor " + vendorVersion + ", GL " + glVersion+"]"); } addStickyQuirkAtMapping(adevice, quirks, quirk, withinGLVersionsMapping); } } } // // RENDERER related quirks // if( isDriverMesa ) { final VersionNumber mesaSafeFBOVersion = new VersionNumber(8, 0, 0); final VersionNumber mesaIntelBuggySharedCtx921 = new VersionNumber(9, 2, 1); final VersionNumber mesaNo10BitColorCompPBuffer = new VersionNumber(18, 0, 0); // Mesa 18.0.0 final VersionNumber mesaSafeGL3Compat = new VersionNumber(18, 2, 0); // Mesa 18.2.0 final VersionNumber mesaSafeDoubleBufferedPBuffer = new VersionNumber(18, 2, 2); // Mesa 18.2.2 final VersionNumber mesaSafeSetSwapIntervalPostRetarget = mesaSafeDoubleBufferedPBuffer; // Mesa 18.2.2 if( vendorVersion.compareTo(mesaSafeSetSwapIntervalPostRetarget) < 0 ) { final int quirk = GLRendererQuirks.NoSetSwapIntervalPostRetarget; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: Renderer " + glRenderer + " / Mesa-Version "+vendorVersion); } quirks.addQuirk( quirk ); } if( vendorVersion.compareTo(mesaNo10BitColorCompPBuffer) >= 0 ) { // FIXME: When is it fixed ?? final int quirk = GLRendererQuirks.No10BitColorCompOffscreen; if(DEBUG) { System.err.println("Quirks: "+GLRendererQuirks.toString(quirk)+": cause: Renderer " + glRenderer + " / Mesa-Version "+vendorVersion); } quirks.addQuirk( quirk ); } if( hwAccel ) { // hardware-acceleration if( vendorVersion.compareTo(mesaSafeDoubleBufferedPBuffer) < 0 ) { final int quirk = GLRendererQuirks.NoDoubleBufferedPBuffer; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: Renderer " + glRenderer + " / Mesa-Version "+vendorVersion); } quirks.addQuirk( quirk ); } } else { // software if( vendorVersion.compareTo(mesaSafeFBOVersion) < 0 ) { // FIXME: Is it fixed in >= 8.0.0 ? final int quirk1 = GLRendererQuirks.BuggyColorRenderbuffer; final int quirk2 = GLRendererQuirks.NoFullFBOSupport; if(DEBUG) { System.err.println("Quirks: "+GLRendererQuirks.toString(quirk1)+", "+GLRendererQuirks.toString(quirk2)+": cause: Renderer " + glRenderer + " / Mesa-Version "+vendorVersion); } quirks.addQuirk( quirk1 ); quirks.addQuirk( quirk2 ); } } if ( compatCtx && (hasMajor > 3 || (hasMajor == 3 && hasMinor >= 1)) && vendorVersion.compareTo(mesaSafeGL3Compat) < 0 ) { final int quirk = GLRendererQuirks.GL3CompatNonCompliant; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: Renderer " + glRenderer + " / Mesa-Version "+vendorVersion); } quirks.addQuirk( quirk ); } if( glRenderer.contains( MesaRendererIntelsp ) && vendorVersion.compareTo(mesaIntelBuggySharedCtx921) >= 0 && isX11 ) { // FIXME: When is it fixed ?? final int quirk = GLRendererQuirks.GLSharedContextBuggy; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: X11 / Renderer " + glRenderer + " / Mesa-Version "+vendorVersion); } quirks.addQuirk( quirk ); } if( glVendor.contains( "nouveau" ) // FIXME: && vendorVersion.compareTo(nouveauBuggyMSAAFixed) < 0 ) { final int quirk = GLRendererQuirks.NoMultiSamplingBuffers; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: X11 / Renderer " + glRenderer + " / Vendor "+glVendor); } addStickyQuirkAtMapping(adevice, quirks, quirk, withinGLVersionsMapping); } if( isWindows && glRenderer.contains("SVGA3D") && vendorVersion.compareTo(mesaSafeFBOVersion) < 0 ) { final int quirk = GLRendererQuirks.NoFullFBOSupport; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType() + " / Renderer " + glRenderer + " / Mesa-Version "+vendorVersion); } quirks.addQuirk( quirk ); } } if(DEBUG) { System.err.println("Quirks local.0: "+quirks); } { // Merge sticky quirks, thread safe due to single threaded initialization! GLRendererQuirks.pushStickyDeviceQuirks(adevice, quirks); final AbstractGraphicsDevice factoryDefaultDevice = factory.getDefaultDevice(); if( !GLRendererQuirks.areSameStickyDevice(factoryDefaultDevice, adevice) ) { GLRendererQuirks.pushStickyDeviceQuirks(factoryDefaultDevice, quirks); } if( isES ) { final GLDrawableFactory mobileFactory = GLDrawableFactory.getFactory(true); if( null != factory ) { final AbstractGraphicsDevice esFactoryDefaultDevice = mobileFactory.getDefaultDevice(); if( !GLRendererQuirks.areSameStickyDevice(esFactoryDefaultDevice, adevice) && !GLRendererQuirks.areSameStickyDevice(esFactoryDefaultDevice, factoryDefaultDevice) ) { GLRendererQuirks.pushStickyDeviceQuirks(esFactoryDefaultDevice, quirks); } } } } glRendererQuirks = quirks; if(DEBUG) { System.err.println("Quirks local.X: "+glRendererQuirks); System.err.println("Quirks sticky on "+adevice+": "+GLRendererQuirks.getStickyDeviceQuirks(adevice)); } } private static final boolean hasFBOImpl(final int major, final int ctp, final ExtensionAvailabilityCache extCache) { return ( 0 != (ctp & CTX_PROFILE_ES) && major >= 2 ) || // ES >= 2.0 major >= 3 || // any >= 3.0 GL ctx (core, compat and ES) ( null != extCache && ( extCache.isExtensionAvailable(GLExtensions.ARB_ES2_compatibility) || // ES 2.0 compatible extCache.isExtensionAvailable(GLExtensions.ARB_framebuffer_object) || // ARB_framebuffer_object extCache.isExtensionAvailable(GLExtensions.EXT_framebuffer_object) || // EXT_framebuffer_object extCache.isExtensionAvailable(GLExtensions.OES_framebuffer_object) // OES_framebuffer_object ) ); } private final void removeCachedVersion(final int major, final int minor, int ctxProfileBits) { if(!isCurrentContextHardwareRasterizer()) { ctxProfileBits |= GLContext.CTX_IMPL_ACCEL_SOFT; } final AbstractGraphicsConfiguration aconfig = drawable.getNativeSurface().getGraphicsConfiguration(); final AbstractGraphicsDevice adevice = aconfig.getScreen().getDevice(); contextFQN = getContextFQN(adevice, major, minor, ctxProfileBits); if (DEBUG) { System.err.println(getThreadName() + ": RM Context FQN: "+contextFQN+" - "+GLContext.getGLVersion(major, minor, ctxProfileBits, null)); } synchronized(mappedContextTypeObjectLock) { final ProcAddressTable table = mappedGLProcAddress.remove( contextFQN ); if(DEBUG) { final int hc = null != table ? table.hashCode() : 0; System.err.println(getThreadName() + ": RM GLContext GL ProcAddressTable mapping key("+contextFQN+") -> "+toHexString(hc)); } } synchronized(mappedContextTypeObjectLock) { final ExtensionAvailabilityCache eCache = mappedExtensionAvailabilityCache.remove( contextFQN ); if(DEBUG) { final int hc = null != eCache ? eCache.hashCode() : 0; System.err.println(getThreadName() + ": RM GLContext GL ExtensionAvailabilityCache mapping key("+contextFQN+") -> "+toHexString(hc)); } } } private final boolean isCurrentContextHardwareRasterizer() { boolean isHardwareRasterizer = true; if(!drawable.getChosenGLCapabilities().getHardwareAccelerated()) { isHardwareRasterizer = false; } else { isHardwareRasterizer = ! ( glRendererLowerCase.contains("software") /* Mesa3D, Apple */ || glRendererLowerCase.contains("mesa x11") /* Mesa3D */ || glRendererLowerCase.contains("softpipe") /* Gallium */ || glRendererLowerCase.contains("llvmpipe") /* Gallium */ ); } return isHardwareRasterizer; } protected abstract StringBuilder getPlatformExtensionsStringImpl(); @Override public final boolean isFunctionAvailable(final String glFunctionName) { // Check GL 1st (cached) if( null != glProcAddressTable ) { // null if this context wasn't not created try { if( glProcAddressTable.isFunctionAvailable( glFunctionName ) ) { return true; } } catch (final Exception e) {} } // Check platform extensions 2nd (cached) - context had to be enabled once final ProcAddressTable pTable = getPlatformExtProcAddressTable(); if(null!=pTable) { try { if( pTable.isFunctionAvailable( glFunctionName ) ) { return true; } } catch (final Exception e) {} } // dynamic function lookup at last incl name aliasing (not cached) final DynamicLookupHelper dynLookup = getGLDynamicLookupHelper(ctxVersion.getMajor(), ctxOptions); if( null == dynLookup ) { throw new GLException("No GLDynamicLookupHelper for "+this); } final String tmpBase = GLNameResolver.normalizeVEN(GLNameResolver.normalizeARB(glFunctionName, true), true); return SecurityUtil.doPrivileged(new PrivilegedAction() { @Override public Boolean run() { boolean res = false; dynLookup.claimAllLinkPermission(); try { final int variants = GLNameResolver.getFuncNamePermutationNumber(tmpBase); for(int i = 0; !res && i < variants; i++) { final String tmp = GLNameResolver.getFuncNamePermutation(tmpBase, i); try { res = dynLookup.isFunctionAvailable(tmp); } catch (final Exception e) { } } } finally { dynLookup.releaseAllLinkPermission(); } return Boolean.valueOf(res); } } ).booleanValue(); } @Override public final boolean isExtensionAvailable(final String glExtensionName) { if(null!=extensionAvailability) { return extensionAvailability.isExtensionAvailable(mapToRealGLExtensionName(glExtensionName)); } return false; } @Override public final int getPlatformExtensionCount() { return null != extensionAvailability ? extensionAvailability.getPlatformExtensionCount() : 0; } @Override public final String getPlatformExtensionsString() { if(null!=extensionAvailability) { return extensionAvailability.getPlatformExtensionsString(); } return null; } @Override public final int getGLExtensionCount() { return null != extensionAvailability ? extensionAvailability.getGLExtensionCount() : 0; } @Override public final String getGLExtensionsString() { if(null!=extensionAvailability) { return extensionAvailability.getGLExtensionsString(); } return null; } public final boolean isExtensionCacheInitialized() { if(null!=extensionAvailability) { return extensionAvailability.isInitialized(); } return false; } protected static String getContextFQN(final AbstractGraphicsDevice device, final int major, final int minor, int ctxProfileBits) { // remove non-key values ctxProfileBits &= CTX_IMPL_CACHE_MASK; return device.getUniqueID() + "-" + toHexString(composeBits(major, minor, ctxProfileBits)); } protected final String getContextFQN() { return contextFQN; } @Override public int getDefaultPixelDataType() { evalPixelDataType(); return pixelDataType; } @Override public int getDefaultPixelDataFormat() { evalPixelDataType(); return pixelDataFormat; } private final void evalPixelDataType() { if(!pixelDataEvaluated) { // only valid while context is made current boolean ok = false; /* if(isGL2GL3() && 3 == components) { pixelDataInternalFormat=GL.GL_RGB; pixelDataFormat=GL.GL_RGB; pixelDataType = GL.GL_UNSIGNED_BYTE; ok = true; } else */ if( isGLES2Compatible() || isExtensionAvailable(GLExtensions.OES_read_format) ) { final int[] glImplColorReadVals = new int[] { 0, 0 }; gl.glGetIntegerv(GL.GL_IMPLEMENTATION_COLOR_READ_FORMAT, glImplColorReadVals, 0); gl.glGetIntegerv(GL.GL_IMPLEMENTATION_COLOR_READ_TYPE, glImplColorReadVals, 1); // pixelDataInternalFormat = (4 == components) ? GL.GL_RGBA : GL.GL_RGB; pixelDataFormat = glImplColorReadVals[0]; pixelDataType = glImplColorReadVals[1]; ok = 0 != pixelDataFormat && 0 != pixelDataType; } if( !ok ) { // RGBA read is safe for all GL profiles // pixelDataInternalFormat = (4 == components) ? GL.GL_RGBA : GL.GL_RGB; pixelDataFormat=GL.GL_RGBA; pixelDataType = GL.GL_UNSIGNED_BYTE; } // TODO: Consider: // return gl.isGL2GL3()?GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV:GL.GL_UNSIGNED_SHORT_5_5_5_1; pixelDataEvaluated = true; } } //---------------------------------------------------------------------- // SwapBuffer @Override public final boolean setSwapInterval(final int interval) throws GLException { validateCurrent(); return setSwapIntervalNC(interval); } protected final boolean setSwapIntervalNC(final int interval) throws GLException { if( !drawableRetargeted || !hasRendererQuirk(GLRendererQuirks.NoSetSwapIntervalPostRetarget) ) { final Integer usedInterval = setSwapIntervalImpl2(interval); if( null != usedInterval ) { currentSwapInterval = usedInterval.intValue(); return true; } } return false; } protected abstract Integer setSwapIntervalImpl2(final int interval); @Override public final int getSwapInterval() { return currentSwapInterval; } @Override protected final void setDefaultSwapInterval() { currentSwapInterval = 0; setSwapIntervalNC(1); } //---------------------------------------------------------------------- // Helpers for buffer object optimizations public final GLBufferObjectTracker getBufferObjectTracker() { return bufferObjectTracker; } public final GLBufferStateTracker getBufferStateTracker() { return bufferStateTracker; } public final GLStateTracker getGLStateTracker() { return glStateTracker; } //--------------------------------------------------------------------------- // Helpers for context optimization where the last context is left // current on the OpenGL worker thread // /** * Returns true if the given thread is owner, otherwise false. *

* Method exists merely for code validation of {@link #isCurrent()}. *

*/ public final boolean isOwner(final Thread thread) { return lock.isOwner(thread); } /** * Returns true if there are other threads waiting for this GLContext to {@link #makeCurrent()}, otherwise false. *

* Since method does not perform any synchronization, accurate result are returned if lock is hold - only. *

*/ public final boolean hasWaiters() { return lock.getQueueLength()>0; } /** * Returns the number of hold locks. See {@link RecursiveLock#getHoldCount()} for semantics. *

* Since method does not perform any synchronization, accurate result are returned if lock is hold - only. *

*/ public final int getLockCount() { return lock.getHoldCount(); } //--------------------------------------------------------------------------- // Special FBO hook // /** * Tracks {@link GL#GL_FRAMEBUFFER}, {@link GL2GL3#GL_DRAW_FRAMEBUFFER} and {@link GL2GL3#GL_READ_FRAMEBUFFER} * to be returned via {@link #getBoundFramebuffer(int)}. * *

Invoked by {@link GL#glBindFramebuffer(int, int)}.

* *

Assumes valid framebufferName range of [0..{@link Integer#MAX_VALUE}]

* *

Does not throw an exception if target is unknown or framebufferName invalid.

*/ public final void setBoundFramebuffer(final int target, final int framebufferName) { if(0 > framebufferName) { return; // ignore invalid name } switch(target) { case GL.GL_FRAMEBUFFER: case GL.GL_DRAW_FRAMEBUFFER: boundFBOTarget[0] = framebufferName; // draw break; case GL.GL_READ_FRAMEBUFFER: boundFBOTarget[1] = framebufferName; // read break; default: // ignore untracked target } } @Override public final int getBoundFramebuffer(final int target) { switch(target) { case GL.GL_FRAMEBUFFER: case GL.GL_DRAW_FRAMEBUFFER: return boundFBOTarget[0]; // draw case GL.GL_READ_FRAMEBUFFER: return boundFBOTarget[1]; // read default: throw new InternalError("Invalid FBO target name: "+toHexString(target)); } } @Override public final int getDefaultDrawFramebuffer() { return drawable.getDefaultDrawFramebuffer(); } @Override public final int getDefaultReadFramebuffer() { return drawable.getDefaultReadFramebuffer(); } @Override public final int getDefaultDrawBuffer() { return drawable.getDefaultDrawBuffer(gl); } @Override public final int getDefaultReadBuffer() { return drawable.getDefaultReadBuffer(gl, drawableRead != drawable); } //--------------------------------------------------------------------------- // GL_ARB_debug_output, GL_AMD_debug_output helpers // @Override public final String getGLDebugMessageExtension() { return glDebugHandler.getExtension(); } @Override public final boolean isGLDebugMessageEnabled() { return glDebugHandler.isEnabled(); } @Override public final int getContextCreationFlags() { return additionalCtxCreationFlags; } @Override public final void setContextCreationFlags(final int flags) { if(!isCreated()) { additionalCtxCreationFlags = flags & GLContext.CTX_OPTION_DEBUG; } } @Override public final boolean isGLDebugSynchronous() { return glDebugHandler.isSynchronous(); } @Override public final void setGLDebugSynchronous(final boolean synchronous) { glDebugHandler.setSynchronous(synchronous); } @Override public final void enableGLDebugMessage(final boolean enable) throws GLException { if(!isCreated()) { if(enable) { additionalCtxCreationFlags |= GLContext.CTX_OPTION_DEBUG; } else { additionalCtxCreationFlags &= ~GLContext.CTX_OPTION_DEBUG; } } else if(0 != (additionalCtxCreationFlags & GLContext.CTX_OPTION_DEBUG) && null != getGLDebugMessageExtension()) { glDebugHandler.enable(enable); } } @Override public final void addGLDebugListener(final GLDebugListener listener) { glDebugHandler.addListener(listener); } @Override public final void removeGLDebugListener(final GLDebugListener listener) { glDebugHandler.removeListener(listener); } @Override public final void glDebugMessageControl(final int source, final int type, final int severity, final int count, final IntBuffer ids, final boolean enabled) { if(glDebugHandler.isExtensionKHRARB()) { gl.getGL2ES2().glDebugMessageControl(source, type, severity, count, ids, enabled); } else if(glDebugHandler.isExtensionAMD()) { gl.getGL2GL3().glDebugMessageEnableAMD(GLDebugMessage.translateARB2AMDCategory(source, type), severity, count, ids, enabled); } } @Override public final void glDebugMessageControl(final int source, final int type, final int severity, final int count, final int[] ids, final int ids_offset, final boolean enabled) { if(glDebugHandler.isExtensionKHRARB()) { gl.getGL2ES2().glDebugMessageControl(source, type, severity, count, ids, ids_offset, enabled); } else if(glDebugHandler.isExtensionAMD()) { gl.getGL2GL3().glDebugMessageEnableAMD(GLDebugMessage.translateARB2AMDCategory(source, type), severity, count, ids, ids_offset, enabled); } } @Override public final void glDebugMessageInsert(final int source, final int type, final int id, final int severity, final String buf) { final int len = (null != buf) ? buf.length() : 0; if(glDebugHandler.isExtensionKHRARB()) { gl.getGL2ES2().glDebugMessageInsert(source, type, id, severity, len, buf); } else if(glDebugHandler.isExtensionAMD()) { gl.getGL2GL3().glDebugMessageInsertAMD(GLDebugMessage.translateARB2AMDCategory(source, type), severity, id, len, buf); } } /* pp */ final String glGetStringiInt(final int name, final int index) { if( 0 == glGetStringiPtr ) { // should not be reached, since initGLRendererAndGLVersionStrings(..)'s failure should abort caller! throw new InternalError("Not initialized: glGetStringiPtr "+toHexString(glGetStringiPtr)); } return glGetStringiInt(name, index, glGetStringiPtr); } /* pp */ final boolean has_glGetStringiInt() { return 0 != glGetStringiPtr; } /* pp */ final String glGetStringInt(final int name) { if( 0 == glGetStringPtr ) { // should not be reached, since initGLRendererAndGLVersionStrings(..)'s failure should abort caller! throw new InternalError("Not initialized: glGetStringPtr "+toHexString(glGetStringPtr)); } return glGetStringInt(name, glGetStringPtr); } /* pp */ final void glGetIntegervInt(final int pname, final int[] params, final int params_offset) { if( 0 == glGetIntegervPtr ) { // should not be reached, since initGLRendererAndGLVersionStrings(..)'s failure should abort caller! throw new InternalError("Not initialized: glGetIntegerv "+toHexString(glGetIntegervPtr)); } glGetIntegervInt(pname, params, params_offset, glGetIntegervPtr); } /** * Internal bootstraping glGetString(GL_RENDERER). *

* Entry point to C language function: const GLubyte * {@native glGetStringi}(GLenum name, GLuint index)
Part of GL_ES_VERSION_3_0, GL_VERSION_3_0
*

**/ private static native String glGetStringiInt(int name, int index, long procAddress); /** * Internal bootstraping glGetString(GL_RENDERER). *

* Entry point to C language function: const GLubyte * {@native glGetString}(GLenum name)
Part of GL_ES_VERSION_2_0, GL_VERSION_ES_CL_CM, GL_VERSION_1_0
*

*/ private static native String glGetStringInt(int name, long procAddress); /** Internal bootstraping glGetIntegerv(..) for version */ private static native void glGetIntegervInt(int pname, int[] params, int params_offset, long procAddress); } (out_pipe_name); PLUGIN_DEBUG ("ITNP_New: deleted input fifo: %s\n", in_pipe_name); cleanup_out_pipe_name: g_free (out_pipe_name); out_pipe_name = NULL; // cleanup_in_pipe: // Delete input pipe. PLUGIN_DEBUG ("ITNP_New: deleting output fifo: %s\n", out_pipe_name); unlink (in_pipe_name); PLUGIN_DEBUG ("ITNP_New: deleted output fifo: %s\n", out_pipe_name); cleanup_in_pipe_name: g_free (in_pipe_name); in_pipe_name = NULL; done: // Now other threads may re-enter.. unlock the mutex g_mutex_unlock(vm_start_mutex); } NPError ITNP_GetValue (NPP instance, NPPVariable variable, void* value) { PLUGIN_DEBUG ("ITNP_GetValue\n"); NPError np_error = NPERR_NO_ERROR; switch (variable) { // This plugin needs XEmbed support. case NPPVpluginNeedsXEmbed: { PLUGIN_DEBUG ("ITNP_GetValue: returning TRUE for NeedsXEmbed.\n"); bool* bool_value = (bool*) value; *bool_value = true; } break; case NPPVpluginScriptableNPObject: { *(NPObject **)value = get_scriptable_object(instance); } break; default: PLUGIN_ERROR ("Unknown plugin value requested."); np_error = NPERR_GENERIC_ERROR; break; } PLUGIN_DEBUG ("ITNP_GetValue return\n"); return np_error; } NPError ITNP_Destroy (NPP instance, NPSavedData** save) { PLUGIN_DEBUG ("ITNP_Destroy %p\n", instance); ITNPPluginData* data = (ITNPPluginData*) instance->pdata; int id = get_id_from_instance(instance); // Let Java know that this applet needs to be destroyed gchar* msg = (gchar*) g_malloc(512*sizeof(gchar)); // 512 is more than enough. We need < 100 g_sprintf(msg, "instance %d destroy", id); plugin_send_message_to_appletviewer(msg); g_free(msg); msg = NULL; if (data) { // Free plugin data. plugin_data_destroy (instance); } g_hash_table_remove(instance_to_id_map, instance); g_hash_table_remove(id_to_instance_map, GINT_TO_POINTER(id)); IcedTeaPluginUtilities::invalidateInstance(instance); PLUGIN_DEBUG ("ITNP_Destroy return\n"); return NPERR_NO_ERROR; } NPError ITNP_SetWindow (NPP instance, NPWindow* window) { PLUGIN_DEBUG ("ITNP_SetWindow\n"); if (instance == NULL) { PLUGIN_ERROR ("Invalid instance."); return NPERR_INVALID_INSTANCE_ERROR; } gpointer id_ptr = g_hash_table_lookup(instance_to_id_map, instance); gint id = 0; if (id_ptr) { id = GPOINTER_TO_INT(id_ptr); } ITNPPluginData* data = (ITNPPluginData*) instance->pdata; // Simply return if we receive a NULL window. if ((window == NULL) || (window->window == NULL)) { PLUGIN_DEBUG ("ITNP_SetWindow: got NULL window.\n"); return NPERR_NO_ERROR; } if (data->window_handle) { // The window already exists. if (data->window_handle == window->window) { // The parent window is the same as in previous calls. PLUGIN_DEBUG ("ITNP_SetWindow: window already exists.\n"); // Critical region. Read data->appletviewer_mutex and send // a message to the appletviewer. g_mutex_lock (data->appletviewer_mutex); if (jvm_up) { gboolean dim_changed = FALSE; // The window is the same as it was for the last // SetWindow call. if (window->width != data->window_width) { PLUGIN_DEBUG ("ITNP_SetWindow: window width changed.\n"); // The width of the plugin window has changed. // Store the new width. data->window_width = window->width; dim_changed = TRUE; } if (window->height != data->window_height) { PLUGIN_DEBUG ("ITNP_SetWindow: window height changed.\n"); // The height of the plugin window has changed. // Store the new height. data->window_height = window->height; dim_changed = TRUE; } if (dim_changed) { gchar* message = g_strdup_printf ("instance %d width %d height %d", id, window->width, window->height); plugin_send_message_to_appletviewer (message); g_free (message); message = NULL; } } else { // The appletviewer is not running. PLUGIN_DEBUG ("ITNP_SetWindow: appletviewer is not running.\n"); } g_mutex_unlock (data->appletviewer_mutex); } else { // The parent window has changed. This branch does run but // doing nothing in response seems to be sufficient. PLUGIN_DEBUG ("ITNP_SetWindow: parent window changed.\n"); } } else { // Else this is initialization PLUGIN_DEBUG ("ITNP_SetWindow: setting window.\n"); // Critical region. Send messages to appletviewer. g_mutex_lock (data->appletviewer_mutex); // Store the window handle and dimensions data->window_handle = window->window; data->window_width = window->width; data->window_height = window->height; // Now we have everything. Send this data to the Java side plugin_send_initialization_message( data->instance_id, (gulong) data->window_handle, data->window_width, data->window_height, data->parameters_string); g_mutex_unlock (data->appletviewer_mutex); } PLUGIN_DEBUG ("ITNP_SetWindow return\n"); return NPERR_NO_ERROR; } NPError ITNP_NewStream (NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16_t* stype) { PLUGIN_DEBUG ("ITNP_NewStream\n"); PLUGIN_DEBUG ("ITNP_NewStream return\n"); return NPERR_GENERIC_ERROR; } void ITNP_StreamAsFile (NPP instance, NPStream* stream, const char* filename) { PLUGIN_DEBUG ("ITNP_StreamAsFile\n"); PLUGIN_DEBUG ("ITNP_StreamAsFile return\n"); } NPError ITNP_DestroyStream (NPP instance, NPStream* stream, NPReason reason) { PLUGIN_DEBUG ("ITNP_DestroyStream\n"); PLUGIN_DEBUG ("ITNP_DestroyStream return\n"); return NPERR_NO_ERROR; } int32_t ITNP_WriteReady (NPP instance, NPStream* stream) { PLUGIN_DEBUG ("ITNP_WriteReady\n"); PLUGIN_DEBUG ("ITNP_WriteReady return\n"); return 0; } int32_t ITNP_Write (NPP instance, NPStream* stream, int32_t offset, int32_t len, void* buffer) { PLUGIN_DEBUG ("ITNP_Write\n"); PLUGIN_DEBUG ("ITNP_Write return\n"); return 0; } void ITNP_Print (NPP instance, NPPrint* platformPrint) { PLUGIN_DEBUG ("ITNP_Print\n"); PLUGIN_DEBUG ("ITNP_Print return\n"); } int16_t ITNP_HandleEvent (NPP instance, void* event) { PLUGIN_DEBUG ("ITNP_HandleEvent\n"); PLUGIN_DEBUG ("ITNP_HandleEvent return\n"); return 0; } void ITNP_URLNotify (NPP instance, const char* url, NPReason reason, void* notifyData) { PLUGIN_DEBUG ("ITNP_URLNotify\n"); PLUGIN_DEBUG ("ITNP_URLNotify return\n"); } NPError get_cookie_info(const char* siteAddr, char** cookieString, uint32_t* len) { // Only attempt to perform this operation if there is a valid plugin instance if (g_hash_table_size(instance_to_id_map) <= 0) { return NPERR_GENERIC_ERROR; } // getvalueforurl needs an NPP instance. Quite frankly, there is no easy way // to know which instance needs the information, as applets on Java side can // be multi-threaded and the thread making a proxy.cookie request cannot be // easily tracked. // Fortunately, XULRunner does not care about the instance as long as it is // valid. So we just pick the first valid one and use it. Proxy/Cookie // information is not instance specific anyway, it is URL specific. if (browser_functions.getvalueforurl) { gpointer instance=getFirstInTableInstance(instance_to_id_map); return browser_functions.getvalueforurl((NPP) instance, NPNURLVCookie, siteAddr, cookieString, len); } else { return NPERR_GENERIC_ERROR; } return NPERR_NO_ERROR; } static NPError set_cookie_info(const char* siteAddr, const char* cookieString, uint32_t len) { // Only attempt to perform this operation if there is a valid plugin instance if (g_hash_table_size(instance_to_id_map) > 0 && browser_functions.getvalueforurl) { // We arbitrarily use the first valid instance we can grab // For an explanation of the logic behind this, see get_cookie_info gpointer instance = getFirstInTableInstance(instance_to_id_map); return browser_functions.setvalueforurl((NPP) instance, NPNURLVCookie, siteAddr, cookieString, len); } return NPERR_GENERIC_ERROR;; } // HELPER FUNCTIONS static void plugin_data_new (ITNPPluginData** data) { PLUGIN_DEBUG ("plugin_data_new\n"); *data = (ITNPPluginData*) (*browser_functions.memalloc) (sizeof (struct ITNPPluginData)); // appletviewer_alive is false until the applet viewer is spawned. if (*data) memset (*data, 0, sizeof (struct ITNPPluginData)); PLUGIN_DEBUG ("plugin_data_new return\n"); } // Documentbase retrieval. This function gets the current document's // documentbase. This function relies on browser-private data so it // will only work when the plugin is loaded in a Mozilla-based // browser. static gchar* plugin_get_documentbase (NPP instance) { PLUGIN_DEBUG ("plugin_get_documentbase\n"); char const* documentbase = NULL; gchar* documentbase_copy = NULL; // FIXME: This method is not ideal, but there are no known NPAPI call // for this. See thread for more information: // http://www.mail-archive.com/chromium-dev@googlegroups.com/msg04844.html // Additionally, since it is insecure, we cannot use it for making // security decisions. NPObject* window; browser_functions.getvalue(instance, NPNVWindowNPObject, &window); NPVariant location; NPIdentifier location_id = browser_functions.getstringidentifier("location"); browser_functions.getproperty(instance, window, location_id, &location); NPVariant href; NPIdentifier href_id = browser_functions.getstringidentifier("href"); browser_functions.getproperty(instance, NPVARIANT_TO_OBJECT(location), href_id, &href); std::string href_str = IcedTeaPluginUtilities::NPVariantAsString(href); documentbase_copy = g_strdup (href_str.c_str()); // Release references. browser_functions.releasevariantvalue(&href); browser_functions.releasevariantvalue(&location); cleanup_done: PLUGIN_DEBUG ("plugin_get_documentbase return\n"); PLUGIN_DEBUG("plugin_get_documentbase returning: %s\n", documentbase_copy); return documentbase_copy; } // plugin_in_pipe_callback is called when data is available on the // input pipe, or when the appletviewer crashes or is killed. It may // be called after data has been destroyed in which case it simply // returns FALSE to remove itself from the glib main loop. static gboolean plugin_in_pipe_callback (GIOChannel* source, GIOCondition condition, gpointer plugin_data) { PLUGIN_DEBUG ("plugin_in_pipe_callback\n"); gboolean keep_installed = TRUE; if (condition & G_IO_IN) { gchar* message = NULL; if (g_io_channel_read_line (in_from_appletviewer, &message, NULL, NULL, &channel_error) != G_IO_STATUS_NORMAL) { if (channel_error) { PLUGIN_ERROR_TWO ("Failed to read line from input channel", channel_error->message); g_error_free (channel_error); channel_error = NULL; } else PLUGIN_ERROR ("Failed to read line from input channel"); } else { consume_message(message); } g_free (message); message = NULL; keep_installed = TRUE; } if (condition & (G_IO_ERR | G_IO_HUP)) { PLUGIN_DEBUG ("appletviewer has stopped.\n"); keep_installed = FALSE; } PLUGIN_DEBUG ("plugin_in_pipe_callback return\n"); return keep_installed; } static void consume_plugin_message(gchar* message) { // internal plugin related message gchar** parts = g_strsplit (message, " ", 5); if (g_str_has_prefix(parts[1], "PluginProxyInfo")) { gchar* proxy = NULL; uint32_t len; gchar* decoded_url = (gchar*) calloc(strlen(parts[4]) + 1, sizeof(gchar)); IcedTeaPluginUtilities::decodeURL(parts[4], &decoded_url); PLUGIN_DEBUG("parts[0]=%s, parts[1]=%s, reference, parts[3]=%s, parts[4]=%s -- decoded_url=%s\n", parts[0], parts[1], parts[3], parts[4], decoded_url); gchar* proxy_info; proxy_info = g_strconcat ("plugin PluginProxyInfo reference ", parts[3], " ", NULL); if (get_proxy_info(decoded_url, &proxy, &len) == NPERR_NO_ERROR) { proxy_info = g_strconcat (proxy_info, proxy, NULL); } PLUGIN_DEBUG("Proxy info: %s\n", proxy_info); plugin_send_message_to_appletviewer(proxy_info); free(decoded_url); decoded_url = NULL; g_free(proxy_info); proxy_info = NULL; g_free(proxy); proxy = NULL; } else if (g_str_has_prefix(parts[1], "PluginCookieInfo")) { gchar* decoded_url = (gchar*) calloc(strlen(parts[4])+1, sizeof(gchar)); IcedTeaPluginUtilities::decodeURL(parts[4], &decoded_url); gchar* cookie_info = g_strconcat ("plugin PluginCookieInfo reference ", parts[3], " ", NULL); gchar* cookie_string = NULL; uint32_t len; if (get_cookie_info(decoded_url, &cookie_string, &len) == NPERR_NO_ERROR) { cookie_info = g_strconcat (cookie_info, cookie_string, NULL); } PLUGIN_DEBUG("Cookie info: %s\n", cookie_info); plugin_send_message_to_appletviewer(cookie_info); free(decoded_url); decoded_url = NULL; g_free(cookie_info); cookie_info = NULL; g_free(cookie_string); cookie_string = NULL; } else if (g_str_has_prefix(parts[1], "PluginSetCookie")) { // Message structure: plugin PluginSetCookie reference -1 <url> <cookie> gchar** cookie_parts = g_strsplit (message, " ", 6); if (g_strv_length(cookie_parts) < 6) { g_strfreev (parts); g_strfreev (cookie_parts); return; // Defensive, message _should_ be properly formatted } gchar* decoded_url = (gchar*) calloc(strlen(cookie_parts[4])+1, sizeof(gchar)); IcedTeaPluginUtilities::decodeURL(cookie_parts[4], &decoded_url); gchar* cookie_string = cookie_parts[5]; uint32_t len = strlen(cookie_string); if (set_cookie_info(decoded_url, cookie_string, len) == NPERR_NO_ERROR) { PLUGIN_DEBUG("Setting cookie for URL %s to %s\n", decoded_url, cookie_string); } else { PLUGIN_DEBUG("Not able to set cookie for URL %s to %s\n", decoded_url, cookie_string); } free(decoded_url); decoded_url = NULL; g_strfreev (cookie_parts); cookie_parts = NULL; } g_strfreev (parts); parts = NULL; } void consume_message(gchar* message) { PLUGIN_DEBUG (" PIPE: plugin read: %s\n", message); if (g_str_has_prefix (message, "instance")) { ITNPPluginData* data; gchar** parts = g_strsplit (message, " ", -1); guint parts_sz = g_strv_length (parts); int instance_id = atoi(parts[1]); NPP instance = (NPP) g_hash_table_lookup(id_to_instance_map, GINT_TO_POINTER(instance_id)); if (instance_id > 0 && !instance) { PLUGIN_DEBUG("Instance %d is not active. Refusing to consume message \"%s\"\n", instance_id, message); return; } else if (instance) { data = (ITNPPluginData*) instance->pdata; } if (g_str_has_prefix (parts[2], "status")) { // clear the "instance X status" parts strcpy(parts[0], ""); strcpy(parts[1], ""); strcpy(parts[2], ""); // join the rest gchar* status_message = g_strjoinv(" ", parts); PLUGIN_DEBUG ("plugin_in_pipe_callback: setting status %s\n", status_message); (*browser_functions.status) (data->owner, status_message); g_free(status_message); status_message = NULL; } else if (g_str_has_prefix (parts[1], "internal")) { //s->post(message); } else { // All other messages are posted to the bus, and subscribers are // expected to take care of them. They better! java_to_plugin_bus->post(message); } g_strfreev (parts); parts = NULL; } else if (g_str_has_prefix (message, "context")) { java_to_plugin_bus->post(message); } else if (g_str_has_prefix (message, "plugin ")) { consume_plugin_message(message); } else { g_print (" Unable to handle message: %s\n", message); } } void get_instance_from_id(int id, NPP& instance) { instance = (NPP) g_hash_table_lookup(id_to_instance_map, GINT_TO_POINTER(id)); } int get_id_from_instance(NPP instance) { int id = GPOINTER_TO_INT(g_hash_table_lookup(instance_to_id_map, instance)); PLUGIN_DEBUG("Returning id %d for instance %p\n", id, instance); return id; } NPError get_proxy_info(const char* siteAddr, char** proxy, uint32_t* len) { // Only attempt to perform this operation if there is a valid plugin instance if (g_hash_table_size(instance_to_id_map) <= 0) { return NPERR_GENERIC_ERROR; } if (browser_functions.getvalueforurl) { // As in get_cookie_info, we use the first active instance gpointer instance=getFirstInTableInstance(instance_to_id_map); browser_functions.getvalueforurl((NPP) instance, NPNURLVProxy, siteAddr, proxy, len); } else { return NPERR_GENERIC_ERROR; } return NPERR_NO_ERROR; } // plugin_out_pipe_callback is called when the appletviewer crashes or // is killed. It may be called after data has been destroyed in which // case it simply returns FALSE to remove itself from the glib main // loop. static gboolean plugin_out_pipe_callback (GIOChannel* source, GIOCondition condition, gpointer plugin_data) { PLUGIN_DEBUG ("plugin_out_pipe_callback\n"); ITNPPluginData* data = (ITNPPluginData*) plugin_data; PLUGIN_DEBUG ("plugin_out_pipe_callback: appletviewer has stopped.\n"); PLUGIN_DEBUG ("plugin_out_pipe_callback return\n"); return FALSE; } // remove all components from LD_LIBRARY_PATH, which start with // MOZILLA_FIVE_HOME; firefox has its own NSS based security provider, // which conflicts with the one configured in nss.cfg. static gchar* plugin_filter_ld_library_path(gchar *path_old) { gchar *moz_home = g_strdup (g_getenv ("MOZILLA_FIVE_HOME")); gchar *moz_prefix; gchar *path_new; gchar** components; int i1, i2; if (moz_home == NULL || path_old == NULL || strlen (path_old) == 0) return path_old; if (g_str_has_suffix (moz_home, "/")) moz_home[strlen (moz_home - 1)] = '\0'; moz_prefix = g_strconcat (moz_home, "/", NULL); components = g_strsplit (path_old, ":", -1); for (i1 = 0, i2 = 0; components[i1] != NULL; i1++) { if (g_strcmp0 (components[i1], moz_home) == 0 || g_str_has_prefix (components[i1], moz_home)) components[i2] = components[i1]; else components[i2++] = components[i1]; } components[i2] = NULL; if (i1 > i2) path_new = g_strjoinv (":", components); g_strfreev (components); g_free (moz_home); g_free (moz_prefix); g_free (path_old); if (path_new == NULL || strlen (path_new) == 0) { PLUGIN_DEBUG("Unset LD_LIBRARY_PATH\n"); return NULL; } else { PLUGIN_DEBUG ("Set LD_LIBRARY_PATH: %s\n", path_new); return path_new; } } // build the environment to pass to the external plugin process static gchar** plugin_filter_environment(void) { gchar **var_names = g_listenv(); gchar **new_env = (gchar**) malloc(sizeof(gchar*) * (g_strv_length (var_names) + 1)); int i_var, i_env; for (i_var = 0, i_env = 0; var_names[i_var] != NULL; i_var++) { gchar *env_value = g_strdup (g_getenv (var_names[i_var])); if (g_str_has_prefix (var_names[i_var], "LD_LIBRARY_PATH")) env_value = plugin_filter_ld_library_path (env_value); if (env_value != NULL) { new_env[i_env++] = g_strdup_printf ("%s=%s", var_names[i_var], env_value); g_free (env_value); } } new_env[i_env] = NULL; return new_env; } static NPError plugin_test_appletviewer () { PLUGIN_DEBUG ("plugin_test_appletviewer: %s\n", get_plugin_executable().c_str()); NPError error = NPERR_NO_ERROR; gchar* command_line[3] = { NULL, NULL, NULL }; gchar** environment; command_line[0] = g_strdup (get_plugin_executable().c_str()); command_line[1] = g_strdup("-version"); command_line[2] = NULL; environment = plugin_filter_environment(); if (!g_spawn_async (NULL, command_line, environment, (GSpawnFlags) 0, NULL, NULL, NULL, &channel_error)) { if (channel_error) { PLUGIN_ERROR_TWO ("Failed to spawn applet viewer", channel_error->message); g_error_free (channel_error); channel_error = NULL; } else PLUGIN_ERROR ("Failed to spawn applet viewer"); error = NPERR_GENERIC_ERROR; } g_strfreev (environment); g_free (command_line[0]); command_line[0] = NULL; g_free (command_line[1]); command_line[1] = NULL; g_free (command_line[2]); command_line[2] = NULL; PLUGIN_DEBUG ("plugin_test_appletviewer return\n"); return error; } static NPError plugin_start_appletviewer (ITNPPluginData* data) { PLUGIN_DEBUG ("plugin_start_appletviewer\n"); NPError error = NPERR_NO_ERROR; std::vector<std::string> command_line; gchar** environment = NULL; std::vector<std::string*>* jvm_args = get_jvm_args(); // Construct command line parameters command_line.push_back(get_plugin_executable()); //Add JVM args to command_line for (int i = 0; i < jvm_args->size(); i++) { command_line.push_back(*jvm_args->at(i)); } command_line.push_back(PLUGIN_BOOTCLASSPATH); // set the classpath to avoid using the default (cwd). command_line.push_back("-classpath"); command_line.push_back(get_plugin_rt_jar()); // Enable coverage agent if we are running instrumented plugin #ifdef COVERAGE_AGENT command_line.push_back(COVERAGE_AGENT); #endif if (plugin_debug) { command_line.push_back("-Xdebug"); command_line.push_back("-Xnoagent"); //Debug flags std::string debug_flags = "-Xrunjdwp:transport=dt_socket,address=8787,server=y,"; debug_flags += plugin_debug_suspend ? "suspend=y" : "suspend=n"; command_line.push_back(debug_flags); } command_line.push_back("sun.applet.PluginMain"); command_line.push_back(out_pipe_name); command_line.push_back(in_pipe_name); // Finished command line parameters environment = plugin_filter_environment(); std::vector<gchar*> vector_gchar = IcedTeaPluginUtilities::vectorStringToVectorGchar(&command_line); gchar **command_line_args = &vector_gchar[0]; if (!g_spawn_async (NULL, command_line_args, environment, (GSpawnFlags) G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &appletviewer_pid, &channel_error)) { if (channel_error) { PLUGIN_ERROR_TWO ("Failed to spawn applet viewer", channel_error->message); g_error_free (channel_error); channel_error = NULL; } else PLUGIN_ERROR ("Failed to spawn applet viewer"); error = NPERR_GENERIC_ERROR; } //Free memory g_strfreev(environment); IcedTeaPluginUtilities::freeStringPtrVector(jvm_args); jvm_args = NULL; command_line_args = NULL; if (appletviewer_pid) { PLUGIN_DEBUG("Initialized VM with pid=%d\n", appletviewer_pid); appletviewer_watch_id = g_child_watch_add(appletviewer_pid, (GChildWatchFunc) appletviewer_monitor, (gpointer) appletviewer_pid); } PLUGIN_DEBUG ("plugin_start_appletviewer return\n"); return error; } /* * Returns JVM options set in itw-settings */ std::vector<std::string*>* get_jvm_args() { std::string output; std::vector<std::string*>* tokenOutput = NULL; bool args_defined = read_deploy_property_value("deployment.plugin.jvm.arguments", output); if (!args_defined){ return new std::vector<std::string*>(); } tokenOutput = IcedTeaPluginUtilities::strSplit(output.c_str(), " \n"); return tokenOutput; } /* * Escape characters for passing to Java. * "\n" for new line, "\\" for "\", "\:" for ";" */ std::string escape_parameter_string(const char* to_encode) { std::string encoded; if (to_encode == NULL) { return encoded; } size_t length = strlen(to_encode); for (int i = 0; i < length; i++) { if (to_encode[i] == '\n') encoded += "\\n"; else if (to_encode[i] == '\\') encoded += "\\\\"; else if (to_encode[i] == ';') encoded += "\\:"; else encoded += to_encode[i]; } return encoded; } /* * Build a string containing an encoded list of parameters to send to the applet viewer. * The parameters are separated as 'key1;value1;key2;value2;'. As well, they are * separated and escaped as: * "\n" for new line, "\\" for "\", "\:" for ";" */ std::string plugin_parameters_string (int argc, char* argn[], char* argv[]) { PLUGIN_DEBUG ("plugin_parameters_string\n"); std::string parameters; for (int i = 0; i < argc; i++) { if (argv[i] != NULL) { std::string name_escaped = escape_parameter_string(argn[i]); std::string value_escaped = escape_parameter_string(argv[i]); //Encode parameters and send as 'key1;value1;key2;value2;' etc parameters += name_escaped; parameters += ';'; parameters += value_escaped; parameters += ';'; } } PLUGIN_DEBUG ("plugin_parameters_string return\n"); return parameters; } // plugin_send_message_to_appletviewer must be called while holding // data->appletviewer_mutex. void plugin_send_message_to_appletviewer (gchar const* message) { PLUGIN_DEBUG ("plugin_send_message_to_appletviewer\n"); if (jvm_up) { gchar* newline_message = NULL; gsize bytes_written = 0; // Send message to appletviewer. newline_message = g_strdup_printf ("%s\n", message); // g_io_channel_write_chars will return something other than // G_IO_STATUS_NORMAL if not all the data is written. In that // case we fail rather than retrying. if (g_io_channel_write_chars (out_to_appletviewer, newline_message, -1, &bytes_written, &channel_error) != G_IO_STATUS_NORMAL) { if (channel_error) { PLUGIN_ERROR_TWO ("Failed to write bytes to output channel", channel_error->message); g_error_free (channel_error); channel_error = NULL; } else PLUGIN_ERROR ("Failed to write bytes to output channel"); } if (g_io_channel_flush (out_to_appletviewer, &channel_error) != G_IO_STATUS_NORMAL) { if (channel_error) { PLUGIN_ERROR_TWO ("Failed to flush bytes to output channel", channel_error->message); g_error_free (channel_error); channel_error = NULL; } else PLUGIN_ERROR ("Failed to flush bytes to output channel"); } g_free (newline_message); newline_message = NULL; PLUGIN_DEBUG (" PIPE: plugin wrote: %s\n", message); } PLUGIN_DEBUG ("plugin_send_message_to_appletviewer return\n"); } /* * Sends the initialization message (handle/size/url) to the plugin */ void plugin_send_initialization_message(char* instance, gulong handle, int width, int height, char* url) { PLUGIN_DEBUG ("plugin_send_initialization_message\n"); gchar *window_message = g_strdup_printf ("instance %s handle %ld width %d height %d %s", instance, handle, width, height, url); plugin_send_message_to_appletviewer (window_message); g_free (window_message); window_message = NULL; PLUGIN_DEBUG ("plugin_send_initialization_message return\n"); } // Stop the appletviewer process. When this is called the // appletviewer can be in any of three states: running, crashed or // hung. If the appletviewer is running then sending it "shutdown" // will cause it to exit. This will cause // plugin_out_pipe_callback/plugin_in_pipe_callback to be called and // the input and output channels to be shut down. If the appletviewer // has crashed then plugin_out_pipe_callback/plugin_in_pipe_callback // would already have been called and data->appletviewer_alive cleared // in which case this function simply returns. If the appletviewer is // hung then this function will be successful and the input and output // watches will be removed by plugin_data_destroy. // plugin_stop_appletviewer must be called with // data->appletviewer_mutex held. static void plugin_stop_appletviewer () { PLUGIN_DEBUG ("plugin_stop_appletviewer\n"); if (jvm_up) { // Shut down the appletviewer. gsize bytes_written = 0; if (out_to_appletviewer) { if (g_io_channel_write_chars (out_to_appletviewer, "shutdown", -1, &bytes_written, &channel_error) != G_IO_STATUS_NORMAL) { if (channel_error) { PLUGIN_ERROR_TWO ("Failed to write shutdown message to" " appletviewer", channel_error->message); g_error_free (channel_error); channel_error = NULL; } else PLUGIN_ERROR ("Failed to write shutdown message to"); } if (g_io_channel_flush (out_to_appletviewer, &channel_error) != G_IO_STATUS_NORMAL) { if (channel_error) { PLUGIN_ERROR_TWO ("Failed to write shutdown message to" " appletviewer", channel_error->message); g_error_free (channel_error); channel_error = NULL; } else PLUGIN_ERROR ("Failed to write shutdown message to"); } if (g_io_channel_shutdown (out_to_appletviewer, TRUE, &channel_error) != G_IO_STATUS_NORMAL) { if (channel_error) { PLUGIN_ERROR_TWO ("Failed to shut down appletviewer" " output channel", channel_error->message); g_error_free (channel_error); channel_error = NULL; } else PLUGIN_ERROR ("Failed to shut down appletviewer"); } } if (in_from_appletviewer) { if (g_io_channel_shutdown (in_from_appletviewer, TRUE, &channel_error) != G_IO_STATUS_NORMAL) { if (channel_error) { PLUGIN_ERROR_TWO ("Failed to shut down appletviewer" " input channel", channel_error->message); g_error_free (channel_error); channel_error = NULL; } else PLUGIN_ERROR ("Failed to shut down appletviewer"); } } } jvm_up = FALSE; sleep(2); /* Needed to prevent crashes during debug (when JDWP port is not freed by the kernel right away) */ PLUGIN_DEBUG ("plugin_stop_appletviewer return\n"); } static void appletviewer_monitor(GPid pid, gint status, gpointer data) { PLUGIN_DEBUG ("appletviewer_monitor\n"); jvm_up = FALSE; pid = -1; PLUGIN_DEBUG ("appletviewer_monitor return\n"); } static void plugin_data_destroy (NPP instance) { PLUGIN_DEBUG ("plugin_data_destroy\n"); ITNPPluginData* tofree = (ITNPPluginData*) instance->pdata; // Remove instance from map gpointer id_ptr = g_hash_table_lookup(instance_to_id_map, instance); if (id_ptr) { gint id = GPOINTER_TO_INT(id_ptr); g_hash_table_remove(instance_to_id_map, instance); g_hash_table_remove(id_to_instance_map, id_ptr); } tofree->window_handle = NULL; tofree->window_height = 0; tofree->window_width = 0; // cleanup_appletviewer_mutex: g_mutex_free (tofree->appletviewer_mutex); tofree->appletviewer_mutex = NULL; // cleanup_instance_string: g_free (tofree->instance_id); tofree->instance_id = NULL; // cleanup applet tag g_free (tofree->parameters_string); tofree->parameters_string = NULL; g_free(tofree->source); tofree->source = NULL; // cleanup_data: // Eliminate back-pointer to plugin instance. tofree->owner = NULL; (*browser_functions.memfree) (tofree); tofree = NULL; PLUGIN_DEBUG ("plugin_data_destroy return\n"); } static bool initialize_browser_functions(const NPNetscapeFuncs* browserTable) { #define NPNETSCAPEFUNCS_LAST_FIELD_USED (browserTable->setvalueforurl) //Determine the size in bytes, as a difference of the address past the last used field //And the browser table address size_t usedSize = (char*)(1 + &NPNETSCAPEFUNCS_LAST_FIELD_USED) - (char*)browserTable; // compare the reported size versus the size we required if (browserTable->size < usedSize) { return false; } //Ensure any unused fields are NULL memset(&browser_functions, 0, sizeof(NPNetscapeFuncs)); //browserTable->size can be larger than sizeof(NPNetscapeFuncs) (PR1106) size_t copySize = browserTable->size < sizeof(NPNetscapeFuncs) ? browserTable->size : sizeof(NPNetscapeFuncs); //Copy fields according to given size memcpy(&browser_functions, browserTable, copySize); return true; } /* Set the plugin table to the correct contents, taking care not to write past * the provided object space */ static bool initialize_plugin_table(NPPluginFuncs* pluginTable) { #define NPPLUGINFUNCS_LAST_FIELD_USED (pluginTable->getvalue) //Determine the size in bytes, as a difference of the address past the last used field //And the browser table address size_t usedSize = (char*)(1 + &NPPLUGINFUNCS_LAST_FIELD_USED) - (char*)pluginTable; // compare the reported size versus the size we required if (pluginTable->size < usedSize) return false; pluginTable->version = (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR; pluginTable->size = sizeof (NPPluginFuncs); pluginTable->newp = NPP_NewProcPtr (ITNP_New); pluginTable->destroy = NPP_DestroyProcPtr (ITNP_Destroy); pluginTable->setwindow = NPP_SetWindowProcPtr (ITNP_SetWindow); pluginTable->newstream = NPP_NewStreamProcPtr (ITNP_NewStream); pluginTable->destroystream = NPP_DestroyStreamProcPtr (ITNP_DestroyStream); pluginTable->asfile = NPP_StreamAsFileProcPtr (ITNP_StreamAsFile); pluginTable->writeready = NPP_WriteReadyProcPtr (ITNP_WriteReady); pluginTable->write = NPP_WriteProcPtr (ITNP_Write); pluginTable->print = NPP_PrintProcPtr (ITNP_Print); pluginTable->urlnotify = NPP_URLNotifyProcPtr (ITNP_URLNotify); pluginTable->getvalue = NPP_GetValueProcPtr (ITNP_GetValue);