/**
* Copyright 2012 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:
*
* 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
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* 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.
*/
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.LongObjectHashMap;
import com.jogamp.nativewindow.egl.EGLGraphicsDevice;
/**
* This implementation provides recursive calls to
* {@link EGL#eglInitialize(long, IntBuffer, IntBuffer)} and {@link EGL#eglTerminate(long)},
* where eglInitialize(..)
is issued only for the 1st call per eglDisplay
* and eglTerminate(..)
is issued only for the last call.
*
* 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. *
*/ public class EGLDisplayUtil { protected static final boolean DEBUG = Debug.debug("EGLDisplayUtil"); private static class DpyCounter { final long eglDisplay; final Throwable createdStack; int refCount; private DpyCounter(long eglDisplay) { this.eglDisplay = eglDisplay; this.refCount = 0; this.createdStack = DEBUG ? new Throwable() : null; } @Override public String toString() { return "EGLDisplay[0x"+Long.toHexString(eglDisplay)+": refCnt "+refCount+"]"; } } static final LongObjectHashMap eglDisplayCounter; static { eglDisplayCounter = new LongObjectHashMap(); eglDisplayCounter.setKeyNotFoundValue(null); } /** * @return number of unclosed EGL Displays.nativeDisplayID
.
* If this fails, method retries with nativeDisplayID
{@link EGL#EGL_DEFAULT_DISPLAY} - the fallback mechanism.
* The actual used nativeDisplayID
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
*/
public 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.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];
}
}
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]));
}
/**
* @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
*/
public static synchronized boolean eglTerminate(long eglDisplay) {
if( EGL.EGL_NO_DISPLAY == eglDisplay) {
return false;
}
final boolean res;
final int refCnt;
final DpyCounter d;
{
DpyCounter _d = (DpyCounter) eglDisplayCounter.get(eglDisplay);
if(null == _d) {
_d = null;
refCnt = -1; // n/a
} else {
refCnt = _d.refCount - 1; // 1 - 1 = 0 -> final terminate
}
d = _d;
}
if( 0 == refCnt ) { // no terminate if still in use or already terminated
res = EGL.eglTerminate(eglDisplay);
eglDisplayCounter.remove(eglDisplay);
} else {
if(0 < refCnt) { // no negative refCount
d.refCount = refCnt;
}
res = true;
}
if(DEBUG) {
System.err.println("EGLDisplayUtil.eglTerminate("+EGLContext.toHexString(eglDisplay)+" ...): #"+refCnt+" = "+res);
// Thread.dumpStack();
}
return res;
}
public static final EGLGraphicsDevice.EGLDisplayLifecycleCallback eglLifecycleCallback = new EGLGraphicsDevice.EGLDisplayLifecycleCallback() {
@Override
public long eglGetAndInitDisplay(long[] nativeDisplayID) {
return eglGetDisplayAndInitialize(nativeDisplayID);
}
@Override
public void eglTerminate(long eglDisplayHandle) {
EGLDisplayUtil.eglTerminate(eglDisplayHandle);
}
};
/**
* Returns an uninitialized {@link EGLGraphicsDevice}. User needs to issue {@link EGLGraphicsDevice#open()} before usage.
* * Using {@link #eglGetDisplayAndInitialize(long[])} for the {@link EGLGraphicsDevice#open()} implementation * and {@link #eglTerminate(long)} for {@link EGLGraphicsDevice#close()}. *
** Using the default {@link ToolkitLock}, via {@link NativeWindowFactory#getDefaultToolkitLock(String, long)}. *
* @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); } /** * Returns an uninitialized {@link EGLGraphicsDevice}. User needs to issue {@link EGLGraphicsDevice#open()} before usage. ** Using {@link #eglGetDisplayAndInitialize(long[])} for the {@link EGLGraphicsDevice#open()} implementation * and {@link #eglTerminate(long)} for {@link EGLGraphicsDevice#close()}. *
** Using the default {@link ToolkitLock}, via {@link NativeWindowFactory#getDefaultToolkitLock(String, long)}. *
* @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 { nativeDisplayID = surface.getDisplayHandle(); // 0 == EGL.EGL_DEFAULT_DISPLAY } final AbstractGraphicsDevice adevice = surface.getGraphicsConfiguration().getScreen().getDevice(); return new EGLGraphicsDevice(nativeDisplayID, EGL.EGL_NO_DISPLAY, adevice.getConnection(), adevice.getUnitID(), eglLifecycleCallback); } }