1 /** 2 * Copyright 2012 JogAmp Community. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without modification, are 5 * permitted provided that the following conditions are met: 6 * 7 * 1. Redistributions of source code must retain the above copyright notice, this list of 8 * conditions and the following disclaimer. 9 * 10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list 11 * of conditions and the following disclaimer in the documentation and/or other materials 12 * provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 * 24 * The views and conclusions contained in the software and documentation are those of the 25 * authors and should not be interpreted as representing official policies, either expressed 26 * or implied, of JogAmp Community. 27 */ 28 29 package jogamp.opengl.egl; 30 31 import java.nio.IntBuffer; 32 import java.util.Iterator; 33 34 import com.jogamp.nativewindow.AbstractGraphicsDevice; 35 import com.jogamp.nativewindow.NativeSurface; 36 import com.jogamp.nativewindow.NativeWindowFactory; 37 import com.jogamp.nativewindow.ToolkitLock; 38 import com.jogamp.opengl.GLException; 39 40 import jogamp.opengl.Debug; 41 42 import com.jogamp.common.ExceptionUtils; 43 import com.jogamp.common.nio.Buffers; 44 import com.jogamp.common.util.LongObjectHashMap; 45 import com.jogamp.nativewindow.egl.EGLGraphicsDevice; 46 import com.jogamp.opengl.egl.EGL; 47 48 /** 49 * This implementation provides recursive calls to 50 * {@link EGL#eglInitialize(long, IntBuffer, IntBuffer)} and {@link EGL#eglTerminate(long)}, 51 * where <code>eglInitialize(..)</code> is issued only for the 1st call per <code>eglDisplay</code> 52 * and <code>eglTerminate(..)</code> is issued only for the last call. 53 * <p> 54 * This class is required, due to implementation bugs within EGL where {@link EGL#eglTerminate(long)} 55 * does not mark the resource for deletion when still in use, bug releases them immediately. 56 * </p> 57 */ 58 public class EGLDisplayUtil { 59 private static final boolean DEBUG = Debug.debug("EGLDisplayUtil"); 60 private static boolean useSingletonEGLDisplay = false; 61 private static EGLDisplayRef singletonEGLDisplay = null; 62 63 private static class EGLDisplayRef { 64 final long eglDisplay; 65 final Throwable createdStack; 66 int initRefCount; 67 68 /** 69 * Returns an already opened {@link EGLDisplayRef} or opens a new {@link EGLDisplayRef}. 70 * <p> 71 * Opened {@link EGLDisplayRef}s are mapped against their <code>eglDisplay</code> handle. 72 * </p> 73 * <p> 74 * Method utilizes {@link EGLDisplayRef}'s reference counter, i.e. increases it. 75 * </p> 76 * <p> 77 * An {@link EGLDisplayRef} is <i>opened</i> via {@link EGL#eglInitialize(long, IntBuffer, IntBuffer)}. 78 * </p> 79 */ getOrCreateOpened(final long eglDisplay, final IntBuffer major, final IntBuffer minor)80 static EGLDisplayRef getOrCreateOpened(final long eglDisplay, final IntBuffer major, final IntBuffer minor) { 81 final EGLDisplayRef o = (EGLDisplayRef) openEGLDisplays.get(eglDisplay); 82 if( null == o ) { 83 final boolean ok = EGL.eglInitialize(eglDisplay, major, minor); 84 if( DEBUG ) { 85 System.err.println("EGLDisplayUtil.EGL.eglInitialize 0x"+Long.toHexString(eglDisplay)+" -> "+ok); 86 } 87 if( ok ) { 88 final EGLDisplayRef n = new EGLDisplayRef(eglDisplay); 89 openEGLDisplays.put(eglDisplay, n); 90 n.initRefCount++; 91 if( DEBUG ) { 92 System.err.println("EGLDisplayUtil.EGL.eglInitialize "+n); 93 } 94 if( null == singletonEGLDisplay ) { 95 singletonEGLDisplay = n; 96 } 97 return n; 98 } else { 99 return null; 100 } 101 } else { 102 o.initRefCount++; 103 return o; 104 } 105 } 106 107 /** 108 * Closes an already opened {@link EGLDisplayRef}. 109 * <p> 110 * Method decreases a reference counter and closes the {@link EGLDisplayRef} if it reaches zero. 111 * </p> 112 * <p> 113 * An {@link EGLDisplayRef} is <i>closed</i> via {@link EGL#eglTerminate(long)}. 114 * </p> 115 */ closeOpened(final long eglDisplay, final boolean[] res)116 static EGLDisplayRef closeOpened(final long eglDisplay, final boolean[] res) { 117 final EGLDisplayRef o = (EGLDisplayRef) openEGLDisplays.get(eglDisplay); 118 res[0] = true; 119 if( null != o ) { 120 if( 0 < o.initRefCount ) { // no negative refCount 121 o.initRefCount--; 122 if( 0 == o.initRefCount ) { 123 final boolean ok = EGL.eglTerminate(eglDisplay); 124 if( DEBUG ) { 125 System.err.println("EGLDisplayUtil.EGL.eglTerminate 0x"+Long.toHexString(eglDisplay)+" -> "+ok); 126 System.err.println("EGLDisplayUtil.EGL.eglTerminate "+o); 127 } 128 res[0] = ok; 129 if( o == singletonEGLDisplay ) { 130 singletonEGLDisplay = null; 131 } 132 } 133 } 134 if( 0 >= o.initRefCount ) { 135 openEGLDisplays.remove(eglDisplay); 136 } 137 } 138 return o; 139 } 140 EGLDisplayRef(final long eglDisplay)141 private EGLDisplayRef(final long eglDisplay) { 142 this.eglDisplay = eglDisplay; 143 this.initRefCount = 0; 144 this.createdStack = DEBUG ? new Throwable() : null; 145 } 146 147 @Override toString()148 public String toString() { 149 return "EGLDisplayRef[0x"+Long.toHexString(eglDisplay)+": refCnt "+initRefCount+"]"; 150 } 151 } 152 private static final LongObjectHashMap openEGLDisplays; 153 154 static { 155 openEGLDisplays = new LongObjectHashMap(); 156 openEGLDisplays.setKeyNotFoundValue(null); 157 } 158 159 /** 160 * @return number of unclosed EGL Displays.<br> 161 */ shutdown(final boolean verbose)162 public static int shutdown(final boolean verbose) { 163 if(DEBUG || verbose || openEGLDisplays.size() > 0 ) { 164 System.err.println("EGLDisplayUtil.EGLDisplays: Shutdown (open: "+openEGLDisplays.size()+")"); 165 if(DEBUG) { 166 ExceptionUtils.dumpStack(System.err); 167 } 168 if( openEGLDisplays.size() > 0) { 169 dumpOpenDisplayConnections(); 170 } 171 } 172 return openEGLDisplays.size(); 173 } 174 dumpOpenDisplayConnections()175 public static void dumpOpenDisplayConnections() { 176 System.err.println("EGLDisplayUtil: Open EGL Display Connections: "+openEGLDisplays.size()); 177 int i=0; 178 for(final Iterator<LongObjectHashMap.Entry> iter = openEGLDisplays.iterator(); iter.hasNext(); i++) { 179 final LongObjectHashMap.Entry e = iter.next(); 180 final EGLDisplayRef v = (EGLDisplayRef) e.value; 181 System.err.println("EGLDisplayUtil: Open["+i+"]: 0x"+Long.toHexString(e.key)+": "+v); 182 if(null != v.createdStack) { 183 v.createdStack.printStackTrace(); 184 } 185 } 186 } 187 setSingletonEGLDisplayOnly(final boolean v)188 /* pp */ static synchronized void setSingletonEGLDisplayOnly(final boolean v) { useSingletonEGLDisplay = v; } 189 eglGetDisplay(final long nativeDisplay_id)190 private static synchronized long eglGetDisplay(final long nativeDisplay_id) { 191 if( useSingletonEGLDisplay && null != singletonEGLDisplay ) { 192 if(DEBUG) { 193 System.err.println("EGLDisplayUtil.eglGetDisplay.s: eglDisplay("+EGLContext.toHexString(nativeDisplay_id)+"): "+ 194 EGLContext.toHexString(singletonEGLDisplay.eglDisplay)+ 195 ", "+((EGL.EGL_NO_DISPLAY != singletonEGLDisplay.eglDisplay)?"OK":"Failed")+", singletonEGLDisplay "+singletonEGLDisplay+" (use "+useSingletonEGLDisplay+")"); 196 } 197 return singletonEGLDisplay.eglDisplay; 198 } 199 final long eglDisplay = EGL.eglGetDisplay(nativeDisplay_id); 200 if(DEBUG) { 201 System.err.println("EGLDisplayUtil.eglGetDisplay.X: eglDisplay("+EGLContext.toHexString(nativeDisplay_id)+"): "+ 202 EGLContext.toHexString(eglDisplay)+ 203 ", "+((EGL.EGL_NO_DISPLAY != eglDisplay)?"OK":"Failed")+", singletonEGLDisplay "+singletonEGLDisplay+" (use "+useSingletonEGLDisplay+")"); 204 } 205 return eglDisplay; 206 } 207 208 /** 209 * @param eglDisplay 210 * @param major 211 * @param minor 212 * @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 213 * 214 * @see EGL#eglInitialize(long, IntBuffer, IntBuffer) 215 */ eglInitialize(final long eglDisplay, final int[] major, final int[] minor)216 private static synchronized boolean eglInitialize(final long eglDisplay, final int[] major, final int[] minor) { 217 if( EGL.EGL_NO_DISPLAY == eglDisplay) { 218 return false; 219 } 220 final EGLDisplayRef d = EGLDisplayRef.getOrCreateOpened(eglDisplay, _eglMajorVersion, _eglMinorVersion); 221 final int _major = _eglMajorVersion.get(0); 222 final int _minor = _eglMinorVersion.get(0); 223 if( null != major && null != minor ) { 224 if( null != d ) { 225 major[0] = _major; 226 minor[0] = _minor; 227 } else { 228 major[0] = 0; 229 minor[0] = 0; 230 } 231 } 232 if(DEBUG) { 233 System.err.println("EGLDisplayUtil.eglInitialize("+EGLContext.toHexString(eglDisplay)+" ...): "+d+" = "+(null != d)+", eglVersion "+_major+"."+_minor+", singletonEGLDisplay "+singletonEGLDisplay+" (use "+useSingletonEGLDisplay+")"); 234 // Thread.dumpStack(); 235 } 236 return null != d; 237 } 238 private static final IntBuffer _eglMajorVersion = Buffers.newDirectIntBuffer(1); 239 private static final IntBuffer _eglMinorVersion = Buffers.newDirectIntBuffer(1); 240 241 /** 242 * @param nativeDisplayID 243 * @param eglDisplay array of size 1 holding return value if successful, otherwise {@link EGL#EGL_NO_DISPLAY}. 244 * @param eglErr array of size 1 holding the EGL error value as retrieved by {@link EGL#eglGetError()} if not successful. 245 * @param major 246 * @param minor 247 * @return {@link EGL#EGL_SUCCESS} if successful, otherwise {@link EGL#EGL_BAD_DISPLAY} if {@link #eglGetDisplay(long)} failed 248 * or {@link EGL#EGL_NOT_INITIALIZED} if {@link #eglInitialize(long, IntBuffer, IntBuffer)} failed. 249 * 250 * @see #eglGetDisplay(long) 251 * @see #eglInitialize(long, IntBuffer, IntBuffer) 252 */ eglGetDisplayAndInitialize(final long nativeDisplayID, final long[] eglDisplay, final int[] eglErr, final int[] major, final int[] minor)253 private static synchronized int eglGetDisplayAndInitialize(final long nativeDisplayID, final long[] eglDisplay, final int[] eglErr, final int[] major, final int[] minor) { 254 eglDisplay[0] = EGL.EGL_NO_DISPLAY; 255 final long _eglDisplay = eglGetDisplay( nativeDisplayID ); 256 if ( EGL.EGL_NO_DISPLAY == _eglDisplay ) { 257 eglErr[0] = EGL.eglGetError(); 258 return EGL.EGL_BAD_DISPLAY; 259 } 260 if ( !eglInitialize( _eglDisplay, major, minor) ) { 261 eglErr[0] = EGL.eglGetError(); 262 return EGL.EGL_NOT_INITIALIZED; 263 } 264 eglDisplay[0] = _eglDisplay; 265 return EGL.EGL_SUCCESS; 266 } 267 268 /** 269 * Attempts to {@link #eglGetDisplayAndInitialize(long, long[], int[], IntBuffer, IntBuffer)} with given <code>nativeDisplayID</code>. 270 * If this fails, method retries with <code>nativeDisplayID</code> {@link EGL#EGL_DEFAULT_DISPLAY} - the fallback mechanism. 271 * The actual used <code>nativeDisplayID</code> is returned in it's in/out array. 272 * 273 * @throws GLException if {@link EGL#eglGetDisplay(long)} or {@link EGL#eglInitialize(long, int[], int, int[], int)} fails incl fallback 274 * @param nativeDisplayID in/out array of size 1, passing the requested nativeVisualID, may return a different revised nativeVisualID handle 275 * @param major 276 * @param minor 277 * @return the initialized EGL display ID 278 * @throws GLException if not successful 279 */ eglGetDisplayAndInitialize(final long[] nativeDisplayID, final int[] major, final int[] minor)280 private static synchronized long eglGetDisplayAndInitialize(final long[] nativeDisplayID, final int[] major, final int[] minor) { 281 final long[] eglDisplay = new long[1]; 282 final int[] eglError = new int[1]; 283 int eglRes = EGLDisplayUtil.eglGetDisplayAndInitialize(nativeDisplayID[0], eglDisplay, eglError, major, minor); 284 if( EGL.EGL_SUCCESS == eglRes ) { 285 return eglDisplay[0]; 286 } 287 if( EGL.EGL_DEFAULT_DISPLAY != nativeDisplayID[0] ) { // fallback to DEGAULT_DISPLAY 288 if(DEBUG) { 289 System.err.println("EGLDisplayUtil.eglGetAndInitDisplay failed with native "+EGLContext.toHexString(nativeDisplayID[0])+", error "+EGLContext.toHexString(eglRes)+"/"+EGLContext.toHexString(eglError[0])+" - fallback!"); 290 } 291 eglRes = EGLDisplayUtil.eglGetDisplayAndInitialize(EGL.EGL_DEFAULT_DISPLAY, eglDisplay, eglError, major, minor); 292 if( EGL.EGL_SUCCESS == eglRes ) { 293 nativeDisplayID[0] = EGL.EGL_DEFAULT_DISPLAY; 294 return eglDisplay[0]; 295 } 296 } 297 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])); 298 } 299 300 /** 301 * @param eglDisplay the EGL display handle 302 * @return true if the eglDisplay is valid and it's reference counter becomes zero and {@link EGL#eglTerminate(long)} was successful, otherwise false 303 */ eglTerminate(final long eglDisplay)304 private static synchronized boolean eglTerminate(final long eglDisplay) { 305 if( EGL.EGL_NO_DISPLAY == eglDisplay) { 306 return false; 307 } 308 final boolean[] res = new boolean[1]; 309 final EGLDisplayRef d = EGLDisplayRef.closeOpened(eglDisplay, res); 310 if(DEBUG) { 311 System.err.println("EGLDisplayUtil.eglTerminate.X("+EGLContext.toHexString(eglDisplay)+" ...): "+d+" = "+res[0]+", singletonEGLDisplay "+singletonEGLDisplay+" (use "+useSingletonEGLDisplay+")"); 312 // Thread.dumpStack(); 313 } 314 return res[0]; 315 } 316 317 private static final EGLGraphicsDevice.EGLDisplayLifecycleCallback eglLifecycleCallback = new EGLGraphicsDevice.EGLDisplayLifecycleCallback() { 318 @Override 319 public long eglGetAndInitDisplay(final long[] nativeDisplayID, final int[] major, final int[] minor) { 320 return eglGetDisplayAndInitialize(nativeDisplayID, major, minor); 321 } 322 @Override 323 public void eglTerminate(final long eglDisplayHandle) { 324 EGLDisplayUtil.eglTerminate(eglDisplayHandle); 325 } 326 }; 327 328 /** 329 * Returns an uninitialized {@link EGLGraphicsDevice}. User needs to issue {@link EGLGraphicsDevice#open()} before usage. 330 * <p> 331 * Using {@link #eglGetDisplayAndInitialize(long[])} for the {@link EGLGraphicsDevice#open()} implementation 332 * and {@link #eglTerminate(long)} for {@link EGLGraphicsDevice#close()}. 333 * </p> 334 * <p> 335 * Using the default {@link ToolkitLock}, via {@link NativeWindowFactory#getDefaultToolkitLock(String, long)}. 336 * </p> 337 * @param nativeDisplayID 338 * @param connection 339 * @param unitID 340 * @return an uninitialized {@link EGLGraphicsDevice} 341 */ eglCreateEGLGraphicsDevice(final long nativeDisplayID, final String connection, final int unitID)342 public static EGLGraphicsDevice eglCreateEGLGraphicsDevice(final long nativeDisplayID, final String connection, final int unitID) { 343 return new EGLGraphicsDevice(nativeDisplayID, EGL.EGL_NO_DISPLAY, connection, unitID, eglLifecycleCallback); 344 } 345 346 /** 347 * Returns an uninitialized {@link EGLGraphicsDevice}. User needs to issue {@link EGLGraphicsDevice#open()} before usage. 348 * <p> 349 * Using {@link #eglGetDisplayAndInitialize(long[])} for the {@link EGLGraphicsDevice#open()} implementation 350 * and {@link #eglTerminate(long)} for {@link EGLGraphicsDevice#close()}. 351 * </p> 352 * <p> 353 * Using the default {@link ToolkitLock}, via {@link NativeWindowFactory#getDefaultToolkitLock(String, long)}. 354 * </p> 355 * @param adevice 356 * @return an uninitialized {@link EGLGraphicsDevice} 357 */ eglCreateEGLGraphicsDevice(final AbstractGraphicsDevice aDevice)358 public static EGLGraphicsDevice eglCreateEGLGraphicsDevice(final AbstractGraphicsDevice aDevice) { 359 return new EGLGraphicsDevice(aDevice, EGL.EGL_NO_DISPLAY, eglLifecycleCallback); 360 } 361 362 /** 363 * Returns an uninitialized {@link EGLGraphicsDevice}. User needs to issue {@link EGLGraphicsDevice#open()} before usage. 364 * <p> 365 * Using {@link #eglGetDisplayAndInitialize(long[])} for the {@link EGLGraphicsDevice#open()} implementation 366 * and {@link #eglTerminate(long)} for {@link EGLGraphicsDevice#close()}. 367 * </p> 368 * <p> 369 * Using the default {@link ToolkitLock}, via {@link NativeWindowFactory#getDefaultToolkitLock(String, long)}. 370 * </p> 371 * @param surface 372 * @return an uninitialized EGLGraphicsDevice 373 */ eglCreateEGLGraphicsDevice(final NativeSurface surface)374 public static EGLGraphicsDevice eglCreateEGLGraphicsDevice(final NativeSurface surface) { 375 final long nativeDisplayID; 376 if( NativeWindowFactory.TYPE_WINDOWS == NativeWindowFactory.getNativeWindowType(false) ) { 377 nativeDisplayID = surface.getSurfaceHandle(); // don't even ask .. 378 } else { 379 nativeDisplayID = surface.getDisplayHandle(); // 0 == EGL.EGL_DEFAULT_DISPLAY 380 } 381 final AbstractGraphicsDevice adevice = surface.getGraphicsConfiguration().getScreen().getDevice(); 382 return new EGLGraphicsDevice(nativeDisplayID, EGL.EGL_NO_DISPLAY, adevice.getConnection(), adevice.getUnitID(), eglLifecycleCallback); 383 } 384 } 385