diff options
Diffstat (limited to 'src/jogl/classes/jogamp/opengl/egl/EGLDisplayUtil.java')
-rw-r--r-- | src/jogl/classes/jogamp/opengl/egl/EGLDisplayUtil.java | 337 |
1 files changed, 271 insertions, 66 deletions
diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLDisplayUtil.java b/src/jogl/classes/jogamp/opengl/egl/EGLDisplayUtil.java index e09400c09..c5f76f667 100644 --- a/src/jogl/classes/jogamp/opengl/egl/EGLDisplayUtil.java +++ b/src/jogl/classes/jogamp/opengl/egl/EGLDisplayUtil.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions 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. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,7 +20,7 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. @@ -29,105 +29,310 @@ package jogamp.opengl.egl; import java.nio.IntBuffer; +import java.util.Iterator; +import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeSurface; import javax.media.nativewindow.NativeWindowFactory; +import javax.media.nativewindow.ToolkitLock; +import javax.media.opengl.GLException; import jogamp.opengl.Debug; -import com.jogamp.common.util.LongIntHashMap; +import com.jogamp.common.util.LongObjectHashMap; +import com.jogamp.nativewindow.egl.EGLGraphicsDevice; -/** - * This implementation provides recursive calls to +/** + * This implementation provides recursive calls to * {@link EGL#eglInitialize(long, IntBuffer, IntBuffer)} and {@link EGL#eglTerminate(long)}, * where <code>eglInitialize(..)</code> is issued only for the 1st call per <code>eglDisplay</code> * and <code>eglTerminate(..)</code> is issued only for the last call. * <p> * This class is required, due to implementation bugs within EGL where {@link EGL#eglTerminate(long)} - * does not mark the resource for deletion when still in use, bug releases them immediatly. + * does not mark the resource for deletion when still in use, bug releases them immediately. * </p> */ public class EGLDisplayUtil { - protected static final boolean DEBUG = Debug.debug("EGL"); - - static LongIntHashMap eglDisplayCounter; - + private static final boolean DEBUG = Debug.debug("EGLDisplayUtil"); + private static boolean useSingletonEGLDisplay = false; + private static EGLDisplayRef singletonEGLDisplay = null; + + private static class EGLDisplayRef { + final long eglDisplay; + final Throwable createdStack; + int initRefCount; + + /** + * Returns an already opened {@link EGLDisplayRef} or opens a new {@link EGLDisplayRef}. + * <p> + * Opened {@link EGLDisplayRef}s are mapped against their <code>eglDisplay</code> handle. + * </p> + * <p> + * Method utilizes {@link EGLDisplayRef}'s reference counter, i.e. increases it. + * </p> + * <p> + * An {@link EGLDisplayRef} is <i>opened</i> via {@link EGL#eglInitialize(long, IntBuffer, IntBuffer)}. + * </p> + */ + static EGLDisplayRef getOrCreateOpened(final long eglDisplay, final IntBuffer major, final IntBuffer minor) { + EGLDisplayRef o = (EGLDisplayRef) openEGLDisplays.get(eglDisplay); + if( null == o ) { + if( EGL.eglInitialize(eglDisplay, major, minor) ) { + final EGLDisplayRef n = new EGLDisplayRef(eglDisplay); + openEGLDisplays.put(eglDisplay, n); + n.initRefCount++; + if( null == singletonEGLDisplay ) { + singletonEGLDisplay = n; + } + return n; + } else { + return null; + } + } else { + o.initRefCount++; + return o; + } + } + + /** + * Closes an already opened {@link EGLDisplayRef}. + * <p> + * Method decreases a reference counter and closes the {@link EGLDisplayRef} if it reaches zero. + * </p> + * <p> + * An {@link EGLDisplayRef} is <i>closed</i> via {@link EGL#eglTerminate(long)}. + * </p> + */ + static EGLDisplayRef closeOpened(final long eglDisplay, final boolean[] res) { + final EGLDisplayRef o = (EGLDisplayRef) openEGLDisplays.get(eglDisplay); + res[0] = true; + if( null != o ) { + if( 0 < o.initRefCount ) { // no negative refCount + o.initRefCount--; + if( 0 == o.initRefCount ) { + res[0] = EGL.eglTerminate(eglDisplay); + if( o == singletonEGLDisplay ) { + singletonEGLDisplay = null; + } + } + } + if( 0 >= o.initRefCount ) { + openEGLDisplays.remove(eglDisplay); + } + } + return o; + } + + private EGLDisplayRef(long eglDisplay) { + this.eglDisplay = eglDisplay; + this.initRefCount = 0; + this.createdStack = DEBUG ? new Throwable() : null; + } + + @Override + public String toString() { + return "EGLDisplayRef[0x"+Long.toHexString(eglDisplay)+": refCnt "+initRefCount+"]"; + } + } + private static final LongObjectHashMap openEGLDisplays; + static { - eglDisplayCounter = new LongIntHashMap(); - eglDisplayCounter.setKeyNotFoundValue(0); + openEGLDisplays = new LongObjectHashMap(); + openEGLDisplays.setKeyNotFoundValue(null); + } + + /** + * @return number of unclosed EGL Displays.<br> + */ + public static int shutdown(boolean verbose) { + if(DEBUG || verbose || openEGLDisplays.size() > 0 ) { + System.err.println("EGLDisplayUtil.EGLDisplays: Shutdown (open: "+openEGLDisplays.size()+")"); + if(DEBUG) { + Thread.dumpStack(); + } + if( openEGLDisplays.size() > 0) { + dumpOpenDisplayConnections(); + } + } + return openEGLDisplays.size(); + } + + public static void dumpOpenDisplayConnections() { + System.err.println("EGLDisplayUtil: Open EGL Display Connections: "+openEGLDisplays.size()); + int i=0; + for(Iterator<LongObjectHashMap.Entry> iter = openEGLDisplays.iterator(); iter.hasNext(); i++) { + final LongObjectHashMap.Entry e = iter.next(); + final EGLDisplayRef v = (EGLDisplayRef) e.value; + System.err.println("EGLDisplayUtil: Open["+i+"]: 0x"+Long.toHexString(e.key)+": "+v); + if(null != v.createdStack) { + v.createdStack.printStackTrace(); + } + } } - public static long eglGetDisplay(long nativeDisplay_id) { + /* pp */ static synchronized void setSingletonEGLDisplayOnly(boolean v) { useSingletonEGLDisplay = v; } + + private static synchronized long eglGetDisplay(long nativeDisplay_id) { + if( useSingletonEGLDisplay && null != singletonEGLDisplay ) { + if(DEBUG) { + System.err.println("EGLDisplayUtil.eglGetDisplay.s: eglDisplay("+EGLContext.toHexString(nativeDisplay_id)+"): "+ + EGLContext.toHexString(singletonEGLDisplay.eglDisplay)+ + ", "+((EGL.EGL_NO_DISPLAY != singletonEGLDisplay.eglDisplay)?"OK":"Failed")+", singletonEGLDisplay "+singletonEGLDisplay+" (use "+useSingletonEGLDisplay+")"); + } + return singletonEGLDisplay.eglDisplay; + } final long eglDisplay = EGL.eglGetDisplay(nativeDisplay_id); if(DEBUG) { - System.err.println("EGLDisplayUtil.eglGetDisplay(): eglDisplay("+EGLContext.toHexString(nativeDisplay_id)+"): "+ + System.err.println("EGLDisplayUtil.eglGetDisplay.X: eglDisplay("+EGLContext.toHexString(nativeDisplay_id)+"): "+ EGLContext.toHexString(eglDisplay)+ - ", "+((EGL.EGL_NO_DISPLAY != eglDisplay)?"OK":"Failed")); + ", "+((EGL.EGL_NO_DISPLAY != eglDisplay)?"OK":"Failed")+", singletonEGLDisplay "+singletonEGLDisplay+" (use "+useSingletonEGLDisplay+")"); } return eglDisplay; } - - public static long eglGetDisplay(NativeSurface surface, boolean allowFallBackToDefault) { - final long nDisplay; - if( NativeWindowFactory.TYPE_WINDOWS.equals(NativeWindowFactory.getNativeWindowType(false)) ) { - nDisplay = surface.getSurfaceHandle(); // don't even ask .. - } else { - nDisplay = surface.getDisplayHandle(); // 0 == EGL.EGL_DEFAULT_DISPLAY + + /** + * @param eglDisplay + * @param major + * @param minor + * @return true if the eglDisplay is valid and it's reference counter becomes one and {@link EGL#eglInitialize(long, IntBuffer, IntBuffer)} was successful, otherwise false + * + * @see EGL#eglInitialize(long, IntBuffer, IntBuffer) + */ + private static synchronized boolean eglInitialize(long eglDisplay, IntBuffer major, IntBuffer minor) { + if( EGL.EGL_NO_DISPLAY == eglDisplay) { + return false; + } + final EGLDisplayRef d = EGLDisplayRef.getOrCreateOpened(eglDisplay, major, minor); + if(DEBUG) { + System.err.println("EGLDisplayUtil.eglInitialize("+EGLContext.toHexString(eglDisplay)+" ...): "+d+" = "+(null != d)+", singletonEGLDisplay "+singletonEGLDisplay+" (use "+useSingletonEGLDisplay+")"); + // Thread.dumpStack(); + } + return null != d; + } + + /** + * @param nativeDisplayID + * @param eglDisplay array of size 1 holding return value if successful, otherwise {@link EGL#EGL_NO_DISPLAY}. + * @param eglErr array of size 1 holding the EGL error value as retrieved by {@link EGL#eglGetError()} if not successful. + * @param major + * @param minor + * @return {@link EGL#EGL_SUCCESS} if successful, otherwise {@link EGL#EGL_BAD_DISPLAY} if {@link #eglGetDisplay(long)} failed + * or {@link EGL#EGL_NOT_INITIALIZED} if {@link #eglInitialize(long, IntBuffer, IntBuffer)} failed. + * + * @see #eglGetDisplay(long) + * @see #eglInitialize(long, IntBuffer, IntBuffer) + */ + private static synchronized int eglGetDisplayAndInitialize(long nativeDisplayID, long[] eglDisplay, int[] eglErr, IntBuffer major, IntBuffer minor) { + eglDisplay[0] = EGL.EGL_NO_DISPLAY; + final long _eglDisplay = eglGetDisplay( nativeDisplayID ); + if ( EGL.EGL_NO_DISPLAY == _eglDisplay ) { + eglErr[0] = EGL.eglGetError(); + return EGL.EGL_BAD_DISPLAY; } - long eglDisplay = EGLDisplayUtil.eglGetDisplay(nDisplay); - if (eglDisplay == EGL.EGL_NO_DISPLAY && nDisplay != EGL.EGL_DEFAULT_DISPLAY && allowFallBackToDefault) { + if ( !eglInitialize( _eglDisplay, major, minor) ) { + eglErr[0] = EGL.eglGetError(); + return EGL.EGL_NOT_INITIALIZED; + } + eglDisplay[0] = _eglDisplay; + return EGL.EGL_SUCCESS; + } + + /** + * Attempts to {@link #eglGetDisplayAndInitialize(long, long[], int[], IntBuffer, IntBuffer)} with given <code>nativeDisplayID</code>. + * If this fails, method retries with <code>nativeDisplayID</code> {@link EGL#EGL_DEFAULT_DISPLAY} - the fallback mechanism. + * The actual used <code>nativeDisplayID</code> is returned in it's in/out array. + * + * @throws GLException if {@link EGL#eglGetDisplay(long)} or {@link EGL#eglInitialize(long, int[], int, int[], int)} fails incl fallback + * @param nativeDisplayID in/out array of size 1, passing the requested nativeVisualID, may return a different revised nativeVisualID handle + * @return the initialized EGL display ID + * @throws GLException if not successful + */ + private static synchronized long eglGetDisplayAndInitialize(long[] nativeDisplayID) { + final long[] eglDisplay = new long[1]; + final int[] eglError = new int[1]; + int eglRes = EGLDisplayUtil.eglGetDisplayAndInitialize(nativeDisplayID[0], eglDisplay, eglError, null, null); + if( EGL.EGL_SUCCESS == eglRes ) { + return eglDisplay[0]; + } + if( EGL.EGL_DEFAULT_DISPLAY != nativeDisplayID[0] ) { // fallback to DEGAULT_DISPLAY if(DEBUG) { - System.err.println("EGLDisplayUtil.eglGetDisplay(): Fall back to EGL_DEFAULT_DISPLAY"); + System.err.println("EGLDisplayUtil.eglGetAndInitDisplay failed with native "+EGLContext.toHexString(nativeDisplayID[0])+", error "+EGLContext.toHexString(eglRes)+"/"+EGLContext.toHexString(eglError[0])+" - fallback!"); + } + eglRes = EGLDisplayUtil.eglGetDisplayAndInitialize(EGL.EGL_DEFAULT_DISPLAY, eglDisplay, eglError, null, null); + if( EGL.EGL_SUCCESS == eglRes ) { + nativeDisplayID[0] = EGL.EGL_DEFAULT_DISPLAY; + return eglDisplay[0]; } - eglDisplay = EGLDisplayUtil.eglGetDisplay(EGL.EGL_DEFAULT_DISPLAY); } - return eglDisplay; + throw new GLException("Failed to created/initialize EGL display incl. fallback default: native "+EGLContext.toHexString(nativeDisplayID[0])+", error "+EGLContext.toHexString(eglRes)+"/"+EGLContext.toHexString(eglError[0])); } - - public static synchronized boolean eglInitialize(long eglDisplay, int[] major, int major_offset, int[] minor, int minor_offset) { - final boolean res; - final int refCnt = eglDisplayCounter.get(eglDisplay) + 1; // 0 + 1 = 1 -> 1st init - if(1==refCnt) { - res = EGL.eglInitialize(eglDisplay, major, major_offset, minor, minor_offset); - } else { - res = true; + + /** + * @param eglDisplay the EGL display handle + * @return true if the eglDisplay is valid and it's reference counter becomes zero and {@link EGL#eglTerminate(long)} was successful, otherwise false + */ + private static synchronized boolean eglTerminate(long eglDisplay) { + if( EGL.EGL_NO_DISPLAY == eglDisplay) { + return false; } - eglDisplayCounter.put(eglDisplay, refCnt); + final boolean[] res = new boolean[1]; + final EGLDisplayRef d = EGLDisplayRef.closeOpened(eglDisplay, res); if(DEBUG) { - System.err.println("EGL.eglInitialize(0x"+Long.toHexString(eglDisplay)+" ...): #"+refCnt+" = "+res); + System.err.println("EGLDisplayUtil.eglTerminate.X("+EGLContext.toHexString(eglDisplay)+" ...): "+d+" = "+res[0]+", singletonEGLDisplay "+singletonEGLDisplay+" (use "+useSingletonEGLDisplay+")"); + // Thread.dumpStack(); } - return res; + return res[0]; } - - public static synchronized boolean eglInitialize(long eglDisplay, IntBuffer major, IntBuffer minor) { - final boolean res; - final int refCnt = eglDisplayCounter.get(eglDisplay) + 1; // 0 + 1 = 1 -> 1st init - if(1==refCnt) { // only initialize once - res = EGL.eglInitialize(eglDisplay, major, minor); - } else { - res = true; + + private static final EGLGraphicsDevice.EGLDisplayLifecycleCallback eglLifecycleCallback = new EGLGraphicsDevice.EGLDisplayLifecycleCallback() { + @Override + public long eglGetAndInitDisplay(long[] nativeDisplayID) { + return eglGetDisplayAndInitialize(nativeDisplayID); } - eglDisplayCounter.put(eglDisplay, refCnt); - if(DEBUG) { - System.err.println("EGL.eglInitialize(0x"+Long.toHexString(eglDisplay)+" ...): #"+refCnt+" = "+res); + @Override + public void eglTerminate(long eglDisplayHandle) { + EGLDisplayUtil.eglTerminate(eglDisplayHandle); } - return res; + }; + + /** + * Returns an uninitialized {@link EGLGraphicsDevice}. User needs to issue {@link EGLGraphicsDevice#open()} before usage. + * <p> + * Using {@link #eglGetDisplayAndInitialize(long[])} for the {@link EGLGraphicsDevice#open()} implementation + * and {@link #eglTerminate(long)} for {@link EGLGraphicsDevice#close()}. + * </p> + * <p> + * Using the default {@link ToolkitLock}, via {@link NativeWindowFactory#getDefaultToolkitLock(String, long)}. + * </p> + * @param nativeDisplayID + * @param connection + * @param unitID + * @return an uninitialized {@link EGLGraphicsDevice} + */ + public static EGLGraphicsDevice eglCreateEGLGraphicsDevice(long nativeDisplayID, String connection, int unitID) { + return new EGLGraphicsDevice(nativeDisplayID, EGL.EGL_NO_DISPLAY, connection, unitID, eglLifecycleCallback); } - - public static synchronized boolean eglTerminate(long eglDisplay) { - final boolean res; - final int refCnt = eglDisplayCounter.get(eglDisplay) - 1; // 1 - 1 = 0 -> final terminate - if(0==refCnt) { // no terminate if still in use or already terminated - res = EGL.eglTerminate(eglDisplay); + + /** + * Returns an uninitialized {@link EGLGraphicsDevice}. User needs to issue {@link EGLGraphicsDevice#open()} before usage. + * <p> + * Using {@link #eglGetDisplayAndInitialize(long[])} for the {@link EGLGraphicsDevice#open()} implementation + * and {@link #eglTerminate(long)} for {@link EGLGraphicsDevice#close()}. + * </p> + * <p> + * Using the default {@link ToolkitLock}, via {@link NativeWindowFactory#getDefaultToolkitLock(String, long)}. + * </p> + * @param surface + * @return an uninitialized EGLGraphicsDevice + */ + public static EGLGraphicsDevice eglCreateEGLGraphicsDevice(NativeSurface surface) { + final long nativeDisplayID; + if( NativeWindowFactory.TYPE_WINDOWS == NativeWindowFactory.getNativeWindowType(false) ) { + nativeDisplayID = surface.getSurfaceHandle(); // don't even ask .. } else { - res = true; - } - if(0<=refCnt) { // no negative refCount - eglDisplayCounter.put(eglDisplay, refCnt); - } - if(DEBUG) { - System.err.println("EGL.eglTerminate(0x"+Long.toHexString(eglDisplay)+" ...): #"+refCnt+" = "+res); + nativeDisplayID = surface.getDisplayHandle(); // 0 == EGL.EGL_DEFAULT_DISPLAY } - return res; + final AbstractGraphicsDevice adevice = surface.getGraphicsConfiguration().getScreen().getDevice(); + return new EGLGraphicsDevice(nativeDisplayID, EGL.EGL_NO_DISPLAY, adevice.getConnection(), adevice.getUnitID(), eglLifecycleCallback); } } |