/**
* 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.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.
*/
public static int shutdown(boolean verbose) {
if(DEBUG || verbose || eglDisplayCounter.size() > 0 ) {
System.err.println("EGLDisplayUtil.EGLDisplays: Shutdown (open: "+eglDisplayCounter.size()+")");
if(DEBUG) {
Thread.dumpStack();
}
if( eglDisplayCounter.size() > 0) {
dumpOpenDisplayConnections();
}
}
return eglDisplayCounter.size();
}
public static void dumpOpenDisplayConnections() {
System.err.println("EGLDisplayUtil: Open EGL Display Connections: "+eglDisplayCounter.size());
int i=0;
for(Iterator iter = eglDisplayCounter.iterator(); iter.hasNext(); i++) {
final LongObjectHashMap.Entry e = iter.next();
final DpyCounter v = (DpyCounter) 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) {
final long eglDisplay = EGL.eglGetDisplay(nativeDisplay_id);
if(DEBUG) {
System.err.println("EGLDisplayUtil.eglGetDisplay(): eglDisplay("+EGLContext.toHexString(nativeDisplay_id)+"): "+
EGLContext.toHexString(eglDisplay)+
", "+((EGL.EGL_NO_DISPLAY != eglDisplay)?"OK":"Failed"));
}
return eglDisplay;
}
/**
* @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)
*/
public static synchronized boolean eglInitialize(long eglDisplay, IntBuffer major, IntBuffer minor) {
if( EGL.EGL_NO_DISPLAY == eglDisplay) {
return false;
}
final int refCnt;
final DpyCounter d;
{
DpyCounter _d = (DpyCounter) eglDisplayCounter.get(eglDisplay);
if(null == _d) {
_d = new DpyCounter(eglDisplay);
refCnt = 1; // 1st init
} else {
refCnt = _d.refCount + 1;
}
d = _d;
}
final boolean res;
if(1==refCnt) { // only initialize once
res = EGL.eglInitialize(eglDisplay, major, minor);
} else {
res = true;
}
if(res) { // update refCount and map if successfully initialized, only
d.refCount = refCnt;
if(1 == refCnt) {
eglDisplayCounter.put(eglDisplay, d);
}
}
if(DEBUG) {
System.err.println("EGLDisplayUtil.eglInitialize("+EGLContext.toHexString(eglDisplay)+" ...): #"+refCnt+", "+d+" = "+res);
// Thread.dumpStack();
}
return res;
}
/**
* @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)
*/
public static synchronized int eglGetDisplayAndInitialize(long nativeDisplayID, long[] eglDisplay, int[] eglErr, IntBuffer major, IntBuffer minor) {
eglDisplay[0] = EGL.EGL_NO_DISPLAY;
final long _eglDisplay = EGLDisplayUtil.eglGetDisplay( nativeDisplayID );
if ( EGL.EGL_NO_DISPLAY == _eglDisplay ) {
eglErr[0] = EGL.eglGetError();
return EGL.EGL_BAD_DISPLAY;
}
if ( !EGLDisplayUtil.eglInitialize( _eglDisplay, major, minor) ) {
eglErr[0] = EGL.eglGetError();
return EGL.EGL_NOT_INITIALIZED;
}
eglDisplay[0] = _eglDisplay;
return EGL.EGL_SUCCESS;
}
/**
* @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);
}
};
/**
* @param nativeDisplayID
* @param connection
* @param unitID
* @return an initialized EGLGraphicsDevice
* @throws GLException if {@link EGL#eglGetDisplay(long)} or {@link EGL#eglInitialize(long, int[], int, int[], int)} fails
* @see EGLGraphicsDevice#EGLGraphicsDevice(long, long, String, int, com.jogamp.nativewindow.egl.EGLGraphicsDevice.EGLDisplayLifecycleCallback)
*/
public static EGLGraphicsDevice eglCreateEGLGraphicsDevice(long nativeDisplayID, String connection, int unitID) {
final EGLGraphicsDevice eglDisplay = new EGLGraphicsDevice(nativeDisplayID, EGL.EGL_NO_DISPLAY, connection, unitID, eglLifecycleCallback);
eglDisplay.open();
return eglDisplay;
}
/**
* @param surface
* @return an initialized EGLGraphicsDevice
* @throws GLException if {@link EGL#eglGetDisplay(long)} or {@link EGL#eglInitialize(long, int[], int, int[], int)} fails incl fallback
*/
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();
final EGLGraphicsDevice eglDevice = new EGLGraphicsDevice(nativeDisplayID, EGL.EGL_NO_DISPLAY, adevice.getConnection(), adevice.getUnitID(), eglLifecycleCallback);
eglDevice.open();
return eglDevice;
}
}