summaryrefslogtreecommitdiffstats
path: root/src/jogl/classes/jogamp/opengl/egl/EGLDisplayUtil.java
blob: 0577124ebbbc2e5661aead59faf7fb7ae8f06aad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
/**
 * 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 <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.
 * </p>
 */
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.<br>
     */
    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<LongObjectHashMap.Entry> 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);
        }
    };

    /**
     * Using the default {@link ToolkitLock}, via {@link NativeWindowFactory#getDefaultToolkitLock(String, long)}.
     * @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;
    }
}