/* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. */ package javax.media.opengl; import com.jogamp.common.GlueGenVersion; import com.jogamp.common.jvm.JVMUtil; import com.jogamp.common.util.ReflectionUtil; import com.jogamp.common.util.VersionUtil; import com.jogamp.nativewindow.NativeWindowVersion; import com.jogamp.opengl.impl.Debug; import com.jogamp.opengl.impl.GLDrawableFactoryImpl; import com.jogamp.opengl.impl.GLDynamicLookupHelper; import com.jogamp.opengl.impl.DesktopGLDynamicLookupHelper; import com.jogamp.opengl.JoglVersion; import java.util.HashMap; import java.util.Iterator; import java.security.*; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.opengl.fixedfunc.GLPointerFunc; import javax.media.nativewindow.NativeWindowFactory; /** * Specifies the the OpenGL profile. * * This class static singleton initialization queries the availability of all OpenGL Profiles * and instantiates singleton GLProfile objects for each available profile. * * The platform default profile may be used, using {@link GLProfile#GetProfileDefault()}, * or more specialized versions using the other static GetProfile methods. */ public class GLProfile { public static final boolean DEBUG = Debug.debug("GLProfile"); /** * Static one time initialization of JOGL. *

* The parameter firstUIActionOnProcess has an impact on concurrent locking,
* see {@link javax.media.nativewindow.NativeWindowFactory#initSingleton(boolean) NativeWindowFactory.initSingleton(firstUIActionOnProcess)}. *

*

* Applications shall call this methods ASAP, before any other UI invocation.
* You may issue the call in your main class static block, which is the earliest point in your application/applet lifecycle, * or within the main function.
* In case applications are able to initialize JOGL before any other UI action,
* they shall invoke this method with firstUIActionOnProcess=true and benefit from fast native multithreading support on all platforms if possible.

*

* RCP Application (Applet's, Webstart, Netbeans, ..) using JOGL may not be able to initialize JOGL * before the first UI action.
* In such case you shall invoke this method with firstUIActionOnProcess=false.
* On some platforms, notably X11 with AWT usage, JOGL will utilize special locking mechanisms which may slow down your * application.

*

* Remark: NEWT is currently not affected by this behavior, ie always uses native multithreading.

*

* However, in case this method is not invoked, hence GLProfile is not initialized explicitly by the user,
* the first call to {@link #getDefault()}, {@link #get(java.lang.String)}, etc, will initialize with firstUIActionOnProcess=false,
* hence without the possibility to enable native multithreading.
* This is not the recommended way, since it may has a performance impact, but it allows you to run code without explicit initialization.

*

* In case no explicit initialization was invoked and the implicit initialization didn't happen,
* you may encounter the following exception: *

     *      javax.media.opengl.GLException: No default profile available
     * 

* * @param firstUIActionOnProcess Should be true if called before the first UI action of the running program, * otherwise false. */ public static synchronized void initSingleton(final boolean firstUIActionOnProcess) { if(!initialized) { initialized = true; // run the whole static initialization privileged to speed up, // since this skips checking further access AccessController.doPrivileged(new PrivilegedAction() { public Object run() { initProfilesForDefaultDevices(firstUIActionOnProcess); return null; } }); } } /** * Trigger eager initialization of GLProfiles for the given device, * in case it isn't done yet. */ public static void initProfiles(AbstractGraphicsDevice device) { getProfileMap(device); } /** * Manual shutdown method, may be called after your last JOGL use * within the running JVM.
* It releases all temporary created resources, ie issues {@link javax.media.opengl.GLDrawableFactory#shutdown()}.
* The shutdown implementation is called via the JVM shutdown hook, if not manually invoked here.
* Invoke shutdown() manually is recommended, due to the unreliable JVM state within the shutdown hook.
*/ public static synchronized void shutdown() { if(initialized) { initialized = false; GLDrawableFactory.shutdown(); } } // // Query platform available OpenGL implementation // public static boolean isGL4bcAvailable(AbstractGraphicsDevice device) { return null != getProfileMap(device).get(GL4bc); } public static boolean isGL4Available(AbstractGraphicsDevice device) { return null != getProfileMap(device).get(GL4); } public static boolean isGL3bcAvailable(AbstractGraphicsDevice device) { return null != getProfileMap(device).get(GL3bc); } public static boolean isGL3Available(AbstractGraphicsDevice device) { return null != getProfileMap(device).get(GL3); } public static boolean isGL2Available(AbstractGraphicsDevice device) { return null != getProfileMap(device).get(GL2); } public static boolean isGLES2Available(AbstractGraphicsDevice device) { return null != getProfileMap(device).get(GLES2); } public static boolean isGLES1Available(AbstractGraphicsDevice device) { return null != getProfileMap(device).get(GLES1); } public static boolean isGL2ES1Available(AbstractGraphicsDevice device) { return null != getProfileMap(device).get(GL2ES1); } public static boolean isGL2ES2Available(AbstractGraphicsDevice device) { return null != getProfileMap(device).get(GL2ES2); } /** Uses the default device */ public static boolean isGL4bcAvailable() { return isGL4bcAvailable(null); } /** Uses the default device */ public static boolean isGL4Available() { return isGL4Available(null); } /** Uses the default device */ public static boolean isGL3bcAvailable() { return isGL3bcAvailable(null); } /** Uses the default device */ public static boolean isGL3Available() { return isGL3Available(null); } /** Uses the default device */ public static boolean isGL2Available() { return isGL2Available(null); } /** Uses the default device */ public static boolean isGLES2Available() { return isGLES2Available(null); } /** Uses the default device */ public static boolean isGLES1Available() { return isGLES1Available(null); } /** Uses the default device */ public static boolean isGL2ES1Available() { return isGL2ES1Available(null); } /** Uses the default device */ public static boolean isGL2ES2Available() { return isGL2ES2Available(null); } public static String glAvailabilityToString(AbstractGraphicsDevice device) { boolean avail; StringBuffer sb = new StringBuffer(); validateInitialization(); if(null==device) { device = defaultDevice; } sb.append("GLAvailability[Native[GL4bc "); avail=isGL4bcAvailable(device); sb.append(avail); if(avail) { glAvailabilityToString(device, sb, 4, GLContext.CTX_PROFILE_COMPAT); } sb.append(", GL4 "); avail=isGL4Available(device); sb.append(avail); if(avail) { glAvailabilityToString(device, sb, 4, GLContext.CTX_PROFILE_CORE); } sb.append(", GL3bc "); avail=isGL3bcAvailable(device); sb.append(avail); if(avail) { glAvailabilityToString(device, sb, 3, GLContext.CTX_PROFILE_COMPAT); } sb.append(", GL3 "); avail=isGL3Available(device); sb.append(avail); if(avail) { glAvailabilityToString(device, sb, 3, GLContext.CTX_PROFILE_CORE); } sb.append(", GL2 "); avail=isGL2Available(device); sb.append(avail); if(avail) { glAvailabilityToString(device, sb, 2, GLContext.CTX_PROFILE_COMPAT); } sb.append(", GL2ES1 "); sb.append(isGL2ES1Available(device)); sb.append(", GLES1 "); avail=isGLES1Available(device); sb.append(avail); if(avail) { glAvailabilityToString(device, sb, 1, GLContext.CTX_PROFILE_ES); } sb.append(", GL2ES2 "); sb.append(isGL2ES2Available(device)); sb.append(", GLES2 "); avail=isGLES2Available(device); sb.append(avail); if(avail) { glAvailabilityToString(device, sb, 2, GLContext.CTX_PROFILE_ES); } sb.append("], Profiles["); for(Iterator i=getProfileMap(device).values().iterator(); i.hasNext(); ) { sb.append(((GLProfile)i.next()).toString()); sb.append(", "); } sb.append(", default "); sb.append(getDefault(device)); sb.append("]]"); return sb.toString(); } /** Uses the default device */ public static String glAvailabilityToString() { return glAvailabilityToString(null); } // // Public (user-visible) profiles // /** The desktop OpenGL compatibility profile 4.x, with x >= 0, ie GL2 plus GL4.
bc stands for backward compatibility. */ public static final String GL4bc = "GL4bc"; /** The desktop OpenGL core profile 4.x, with x >= 0 */ public static final String GL4 = "GL4"; /** The desktop OpenGL compatibility profile 3.x, with x >= 1, ie GL2 plus GL3.
bc stands for backward compatibility. */ public static final String GL3bc = "GL3bc"; /** The desktop OpenGL core profile 3.x, with x >= 1 */ public static final String GL3 = "GL3"; /** The desktop OpenGL profile 1.x up to 3.0 */ public static final String GL2 = "GL2"; /** The embedded OpenGL profile ES 1.x, with x >= 0 */ public static final String GLES1 = "GLES1"; /** The embedded OpenGL profile ES 2.x, with x >= 0 */ public static final String GLES2 = "GLES2"; /** The intersection of the desktop GL2 and embedded ES1 profile */ public static final String GL2ES1 = "GL2ES1"; /** The intersection of the desktop GL3, GL2 and embedded ES2 profile */ public static final String GL2ES2 = "GL2ES2"; /** The intersection of the desktop GL3 and GL2 profile */ public static final String GL2GL3 = "GL2GL3"; /** The default profile, used for the device default profile map */ private static final String GL_DEFAULT = "GL_DEFAULT"; /** * All GL Profiles in the order of default detection. * Desktop compatibility profiles (the one with fixed function pipeline) comes first. * * FIXME GL3GL4: Due to GL3 and GL4 implementation bugs, we still choose GL2 first, if available! * * * */ public static final String[] GL_PROFILE_LIST_ALL = new String[] { GL2, GL3bc, GL4bc, GL2GL3, GL3, GL4, GL2ES2, GLES2, GL2ES1, GLES1 }; /** * Order of maximum fixed function profiles * * * */ public static final String[] GL_PROFILE_LIST_MAX_FIXEDFUNC = new String[] { GL4bc, GL3bc, GL2, GL2ES1, GLES1 }; /** * Order of maximum programmable shader profiles * * * */ public static final String[] GL_PROFILE_LIST_MAX_PROGSHADER = new String[] { GL4, GL4bc, GL3, GL3bc, GL2, GL2ES2, GLES2 }; /** * All GL2ES2 Profiles in the order of default detection. * * FIXME GL3GL4: Due to GL3 and GL4 implementation bugs, we still choose GL2 first, if available! * * * */ public static final String[] GL_PROFILE_LIST_GL2ES2 = new String[] { GL2ES2, GL2, GL3, GL4, GLES2 }; /** * All GL2ES1 Profiles in the order of default detection. * * FIXME GL3GL4: Due to GL3 and GL4 implementation bugs, we still choose GL2 first, if available! * * * */ public static final String[] GL_PROFILE_LIST_GL2ES1 = new String[] { GL2ES1, GL2, GL3bc, GL4bc, GLES1 }; /** * All GLES Profiles in the order of default detection. * * * */ public static final String[] GL_PROFILE_LIST_GLES = new String[] { GLES2, GLES1 }; /** Returns a default GLProfile object, reflecting the best for the running platform. * It selects the first of the set {@link GLProfile#GL_PROFILE_LIST_ALL} * @see #GL_PROFILE_LIST_ALL */ public static GLProfile getDefault(AbstractGraphicsDevice device) { GLProfile glp = get(device, GL_DEFAULT); return glp; } /** Uses the default device */ public static GLProfile getDefault() { return getDefault(defaultDevice); } /** * Returns the highest profile, implementing the fixed function pipeline * It selects the first of the set: {@link GLProfile#GL_PROFILE_LIST_MAX_FIXEDFUNC} * * @throws GLException if no implementation for the given profile is found. * @see #GL_PROFILE_LIST_MAX_FIXEDFUNC */ public static GLProfile getMaxFixedFunc(AbstractGraphicsDevice device) throws GLException { return get(device, GL_PROFILE_LIST_MAX_FIXEDFUNC); } /** Uses the default device */ public static GLProfile getMaxFixedFunc() throws GLException { return get(GL_PROFILE_LIST_MAX_FIXEDFUNC); } /** * Returns the highest profile, implementing the programmable shader pipeline. * It selects the first of the set: {@link GLProfile#GL_PROFILE_LIST_MAX_PROGSHADER} * * @throws GLException if no implementation for the given profile is found. * @see #GL_PROFILE_LIST_MAX_PROGSHADER */ public static GLProfile getMaxProgrammable(AbstractGraphicsDevice device) throws GLException { return get(device, GL_PROFILE_LIST_MAX_PROGSHADER); } /** Uses the default device */ public static GLProfile getMaxProgrammable() throws GLException { return get(GL_PROFILE_LIST_MAX_PROGSHADER); } /** * Returns a profile, implementing the interface GL2ES1. * It selects the first of the set: {@link GLProfile#GL_PROFILE_LIST_GL2ES1} * * @throws GLException if no implementation for the given profile is found. * @see #GL_PROFILE_LIST_GL2ES1 */ public static GLProfile getGL2ES1(AbstractGraphicsDevice device) throws GLException { return get(device, GL_PROFILE_LIST_GL2ES1); } /** Uses the default device */ public static GLProfile getGL2ES1() throws GLException { return get(GL_PROFILE_LIST_GL2ES1); } /** * Returns a profile, implementing the interface GL2ES2. * It selects the first of the set: {@link GLProfile#GL_PROFILE_LIST_GL2ES2} * * @throws GLException if no implementation for the given profile is found. * @see #GL_PROFILE_LIST_GL2ES2 */ public static GLProfile getGL2ES2(AbstractGraphicsDevice device) throws GLException { return get(device, GL_PROFILE_LIST_GL2ES2); } /** Uses the default device */ public static GLProfile getGL2ES2() throws GLException { return get(GL_PROFILE_LIST_GL2ES2); } /** Returns a GLProfile object. * verifies the given profile and chooses an appropriate implementation. * A generic value of null or GL will result in * the default profile. * * @throws GLException if no implementation for the given profile is found. */ public static GLProfile get(AbstractGraphicsDevice device, String profile) throws GLException { if(null==profile || profile.equals("GL")) { profile = GL_DEFAULT; } return (GLProfile) getProfileMap(device).get(profile); } /** Uses the default device */ public static GLProfile get(String profile) throws GLException { return get(defaultDevice, profile); } /** * Returns the first profile from the given list, * where an implementation is available. * * @throws GLException if no implementation for the given profile is found. */ public static GLProfile get(AbstractGraphicsDevice device, String[] profiles) throws GLException { HashMap map = getProfileMap(device); for(int i=0; i 0) msg.append(", "); msg.append(list[i]); } msg.append("]"); return msg.toString(); } private static void glAvailabilityToString(AbstractGraphicsDevice device, StringBuffer sb, int major, int profile) { String str = GLContext.getAvailableGLVersionAsString(device, major, profile); if(null==str) { throw new GLException("Internal Error"); } sb.append("["); sb.append(str); sb.append("]"); } private static boolean computeProfileMap(AbstractGraphicsDevice device, boolean desktopCtxUndef, boolean eglCtxUndef) { if (DEBUG) { System.err.println("GLProfile.init map "+device.getConnection()+", desktopCtxUndef "+desktopCtxUndef+", eglCtxUndef "+eglCtxUndef); } GLProfile defaultGLProfile = null; HashMap/**/ _mappedProfiles = new HashMap(GL_PROFILE_LIST_ALL.length + 1 /* default */); for(int i=0; i 0; } /** * Returns the profile implementation */ private static String computeProfileImpl(AbstractGraphicsDevice device, String profile, boolean desktopCtxUndef, boolean eglCtxUndef) { if (GL2ES1.equals(profile)) { if(hasGL234Impl) { if(desktopCtxUndef || GLContext.isGL2Available(device)) { return GL2; } else if(GLContext.isGL3bcAvailable(device)) { return GL3bc; } else if(GLContext.isGL4bcAvailable(device)) { return GL4bc; } } if(hasGLES1Impl && ( eglCtxUndef || GLContext.isGLES1Available(device))) { return GLES1; } } else if (GL2ES2.equals(profile)) { if(hasGL234Impl) { if(desktopCtxUndef || GLContext.isGL2Available(device)) { return GL2; } else if(GLContext.isGL3Available(device)) { return GL3; } else if(GLContext.isGL4Available(device)) { return GL4; } } if(hasGLES2Impl && ( eglCtxUndef || GLContext.isGLES2Available(device))) { return GLES2; } } else if(GL2GL3.equals(profile)) { if(hasGL234Impl) { if(desktopCtxUndef || GLContext.isGL2Available(device)) { return GL2; } else if(GLContext.isGL3bcAvailable(device)) { return GL3bc; } else if(GLContext.isGL4bcAvailable(device)) { return GL4bc; } else if(GLContext.isGL3Available(device)) { return GL3; } else if(GLContext.isGL4Available(device)) { return GL4; } } } else if(GL4bc.equals(profile) && hasGL234Impl && ( desktopCtxUndef || GLContext.isGL4bcAvailable(device))) { return GL4bc; } else if(GL4.equals(profile) && hasGL234Impl && ( desktopCtxUndef || GLContext.isGL4Available(device))) { return GL4; } else if(GL3bc.equals(profile) && hasGL234Impl && ( desktopCtxUndef || GLContext.isGL3bcAvailable(device))) { return GL3bc; } else if(GL3.equals(profile) && hasGL234Impl && ( desktopCtxUndef || GLContext.isGL3Available(device))) { return GL3; } else if(GL2.equals(profile) && hasGL234Impl && ( desktopCtxUndef || GLContext.isGL2Available(device))) { return GL2; } else if(GLES2.equals(profile) && hasGLES2Impl && ( eglCtxUndef || GLContext.isGLES2Available(device))) { return GLES2; } else if(GLES1.equals(profile) && hasGLES1Impl && ( eglCtxUndef || GLContext.isGLES1Available(device))) { return GLES1; } return null; } private static String getGLImplBaseClassName(String profileImpl) { if ( GL4bc.equals(profileImpl) || GL4.equals(profileImpl) || GL3bc.equals(profileImpl) || GL3.equals(profileImpl) || GL2.equals(profileImpl) ) { return "com.jogamp.opengl.impl.gl4.GL4bc"; } else if(GLES1.equals(profileImpl) || GL2ES1.equals(profileImpl)) { return "com.jogamp.opengl.impl.es1.GLES1"; } else if(GLES2.equals(profileImpl) || GL2ES2.equals(profileImpl)) { return "com.jogamp.opengl.impl.es2.GLES2"; } else { throw new GLException("unsupported profile \"" + profileImpl + "\""); } } private static /*final*/ HashMap/**/ deviceConn2ProfileMap = new HashMap(); /** * This implementation support lazy initialization, while avoiding recursion/deadlocks.
* If no mapping 'device -> GLProfiles-Map' exists yet, it triggers
* - create empty mapping device -> GLProfiles-Map
* - initialization GLProfiles-Map' * @return the GLProfile HashMap */ private static HashMap getProfileMap(AbstractGraphicsDevice device) { validateInitialization(); if(null==device) { device = defaultDevice; } String deviceKey = device.getUniqueID(); HashMap map = (HashMap) deviceConn2ProfileMap.get(deviceKey); if(null==map) { initProfilesForDevice(device); if( null == deviceConn2ProfileMap.get(deviceKey) ) { throw new InternalError("initProfilesForDevice(..) didn't issue setProfileMap(..) on "+device); } } return map; } private static void setProfileMap(AbstractGraphicsDevice device, HashMap/**/mappedProfiles) { validateInitialization(); synchronized ( deviceConn2ProfileMap ) { deviceConn2ProfileMap.put(device.getUniqueID(), mappedProfiles); } } private GLProfile(String profile, String profileImpl) { this.profile = profile; this.profileImpl = profileImpl; } private String profileImpl = null; private String profile = null; }