Project

General

Profile

Download (10.4 KB) Statistics
| Branch: | Revision:

examples / src / main / java / org / distorted / examples / plainmonalisa / EglCore.java @ 862fcd79

1
package org.distorted.examples.plainmonalisa;
2

    
3
import android.graphics.SurfaceTexture;
4
import android.opengl.EGL14;
5
import android.opengl.EGLConfig;
6
import android.opengl.EGLContext;
7
import android.opengl.EGLDisplay;
8
import android.opengl.EGLExt;
9
import android.opengl.EGLSurface;
10
import android.util.Log;
11
import android.view.Surface;
12

    
13
///////////////////////////////////////////////////////////////////////////////////////////////////
14
/**
15
 * Core EGL state (display, context, config).
16
 * <p>
17
 * The EGLContext must only be attached to one thread at a time.  This class is not thread-safe.
18
 */
19
public final class EglCore
20
  {
21
  private static final String TAG = "EglCore";
22

    
23
  /**
24
   * Constructor flag: surface must be recordable.  This discourages EGL from using a
25
   * pixel format that cannot be converted efficiently to something usable by the video
26
   * encoder.
27
   */
28
  public static final int FLAG_RECORDABLE = 0x01;
29

    
30
  /**
31
   * Constructor flag: ask for GLES3, fall back to GLES2 if not available.  Without this
32
   * flag, GLES2 is used.
33
   */
34
  public static final int FLAG_TRY_GLES3 = 0x02;
35

    
36
  // Android-specific extension.
37
  private static final int EGL_RECORDABLE_ANDROID = 0x3142;
38

    
39
  private EGLDisplay mEGLDisplay = EGL14.EGL_NO_DISPLAY;
40
  private EGLContext mEGLContext = EGL14.EGL_NO_CONTEXT;
41
  private EGLConfig mEGLConfig = null;
42
  private int mGlVersion = -1;
43

    
44
///////////////////////////////////////////////////////////////////////////////////////////////////
45
  /**
46
   * Prepares EGL display and context.
47
   * <p>
48
   * Equivalent to EglCore(null, 0).
49
   */
50
  public EglCore() {
51
        this(null, 0);
52
    }
53

    
54
///////////////////////////////////////////////////////////////////////////////////////////////////
55
  /**
56
   * Prepares EGL display and context.
57
   * <p>
58
   * @param sharedContext The context to share, or null if sharing is not desired.
59
   * @param flags Configuration bit flags, e.g. FLAG_RECORDABLE.
60
   */
61
  public EglCore(EGLContext sharedContext, int flags)
62
    {
63
    if (mEGLDisplay != EGL14.EGL_NO_DISPLAY)
64
      {
65
      throw new RuntimeException("EGL already set up");
66
      }
67

    
68
    if (sharedContext == null)
69
      {
70
      sharedContext = EGL14.EGL_NO_CONTEXT;
71
      }
72

    
73
    mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
74

    
75
    if (mEGLDisplay == EGL14.EGL_NO_DISPLAY)
76
      {
77
      throw new RuntimeException("unable to get EGL14 display");
78
      }
79

    
80
    int[] version = new int[2];
81

    
82
    if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1))
83
      {
84
      mEGLDisplay = null;
85
      throw new RuntimeException("unable to initialize EGL14");
86
      }
87

    
88
    // Try to get a GLES3 context, if requested.
89
    if ((flags & FLAG_TRY_GLES3) != 0)
90
      {
91
      //Log.d(TAG, "Trying GLES 3");
92
      EGLConfig config = getConfig(flags, 3);
93

    
94
      if (config != null)
95
        {
96
        int[] attrib3_list =  { EGL14.EGL_CONTEXT_CLIENT_VERSION, 3, EGL14.EGL_NONE };
97
        EGLContext context = EGL14.eglCreateContext(mEGLDisplay, config, sharedContext, attrib3_list, 0);
98

    
99
        if (EGL14.eglGetError() == EGL14.EGL_SUCCESS)
100
          {
101
          //Log.d(TAG, "Got GLES 3 config");
102
          mEGLConfig = config;
103
          mEGLContext = context;
104
          mGlVersion = 3;
105
          }
106
        }
107
      }
108

    
109
    if (mEGLContext == EGL14.EGL_NO_CONTEXT)
110
      {  // GLES 2 only, or GLES 3 attempt failed
111
         //Log.d(TAG, "Trying GLES 2");
112
      EGLConfig config = getConfig(flags, 2);
113

    
114
      if (config == null)
115
        {
116
        throw new RuntimeException("Unable to find a suitable EGLConfig");
117
        }
118
      int[] attrib2_list = { EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE };
119
      EGLContext context = EGL14.eglCreateContext(mEGLDisplay, config, sharedContext, attrib2_list, 0);
120
      checkEglError("eglCreateContext");
121
      mEGLConfig = config;
122
      mEGLContext = context;
123
      mGlVersion = 2;
124
      }
125

    
126
    // Confirm with query.
127
    int[] values = new int[1];
128
    EGL14.eglQueryContext(mEGLDisplay, mEGLContext, EGL14.EGL_CONTEXT_CLIENT_VERSION, values, 0);
129

    
130
    Log.d(TAG, "EGLContext created, client version " + values[0]);
131
    }
132

    
133
///////////////////////////////////////////////////////////////////////////////////////////////////
134
  /**
135
   * Finds a suitable EGLConfig.
136
   *
137
   * @param flags Bit flags from constructor.
138
   * @param version Must be 2 or 3.
139
   */
140
  private EGLConfig getConfig(int flags, int version)
141
    {
142
    int renderableType = EGL14.EGL_OPENGL_ES2_BIT;
143

    
144
    if (version >= 3)
145
      {
146
      renderableType |= EGLExt.EGL_OPENGL_ES3_BIT_KHR;
147
      }
148

    
149
    // The actual surface is generally RGBA or RGBX, so situationally omitting alpha
150
    // doesn't really help.  It can also lead to a huge performance hit on glReadPixels()
151
    // when reading into a GL_RGBA buffer.
152
    int[] attribList =
153
                {
154
                EGL14.EGL_RED_SIZE, 8,
155
                EGL14.EGL_GREEN_SIZE, 8,
156
                EGL14.EGL_BLUE_SIZE, 8,
157
                EGL14.EGL_ALPHA_SIZE, 8,
158
                //EGL14.EGL_DEPTH_SIZE, 16,
159
                //EGL14.EGL_STENCIL_SIZE, 8,
160
                EGL14.EGL_RENDERABLE_TYPE, renderableType,
161
                EGL14.EGL_NONE, 0,      // placeholder for recordable [@-3]
162
                EGL14.EGL_NONE
163
                };
164

    
165
    if ((flags & FLAG_RECORDABLE) != 0)
166
      {
167
      attribList[attribList.length - 3] = EGL_RECORDABLE_ANDROID;
168
      attribList[attribList.length - 2] = 1;
169
      }
170

    
171
    EGLConfig[] configs = new EGLConfig[1];
172
    int[] numConfigs = new int[1];
173

    
174
    if (!EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0, configs.length, numConfigs, 0))
175
      {
176
      Log.w(TAG, "unable to find RGB8888 / " + version + " EGLConfig");
177
      return null;
178
      }
179

    
180
    return configs[0];
181
    }
182

    
183
///////////////////////////////////////////////////////////////////////////////////////////////////
184
  /**
185
   * Discards all resources held by this class, notably the EGL context.  This must be
186
   * called from the thread where the context was created.
187
   * <p>
188
   * On completion, no context will be current.
189
   */
190
  public void release()
191
    {
192
    if (mEGLDisplay != EGL14.EGL_NO_DISPLAY)
193
      {
194
      // Android is unusual in that it uses a reference-counted EGLDisplay.  So for
195
      // every eglInitialize() we need an eglTerminate().
196
      EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
197
      EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
198
      EGL14.eglReleaseThread();
199
      EGL14.eglTerminate(mEGLDisplay);
200
      }
201

    
202
    mEGLDisplay = EGL14.EGL_NO_DISPLAY;
203
    mEGLContext = EGL14.EGL_NO_CONTEXT;
204
    mEGLConfig = null;
205
    }
206

    
207
///////////////////////////////////////////////////////////////////////////////////////////////////
208
  @Override
209
  protected void finalize() throws Throwable
210
    {
211
    try
212
      {
213
      if (mEGLDisplay != EGL14.EGL_NO_DISPLAY)
214
        {
215
        // We're limited here -- finalizers don't run on the thread that holds
216
        // the EGL state, so if a surface or context is still current on another
217
        // thread we can't fully release it here.  Exceptions thrown from here
218
        // are quietly discarded.  Complain in the log file.
219
        Log.w(TAG, "WARNING: EglCore was not explicitly released -- state may be leaked");
220
        release();
221
        }
222
      }
223
    finally
224
      {
225
      super.finalize();
226
      }
227
    }
228

    
229
///////////////////////////////////////////////////////////////////////////////////////////////////
230
  /**
231
   * Destroys the specified surface.  Note the EGLSurface won't actually be destroyed if it's
232
   * still current in a context.
233
   */
234
  public void releaseSurface(EGLSurface eglSurface)
235
    {
236
    EGL14.eglDestroySurface(mEGLDisplay, eglSurface);
237
    }
238

    
239
///////////////////////////////////////////////////////////////////////////////////////////////////
240
  /**
241
   * Creates an EGL surface associated with a Surface.
242
   * <p>
243
   * If this is destined for MediaCodec, the EGLConfig should have the "recordable" attribute.
244
   */
245
  public EGLSurface createWindowSurface(Object surface)
246
    {
247
    if (!(surface instanceof Surface) && !(surface instanceof SurfaceTexture))
248
      {
249
      throw new RuntimeException("invalid surface: " + surface);
250
      }
251

    
252
    // Create a window surface, and attach it to the Surface we received.
253
    int[] surfaceAttribs = { EGL14.EGL_NONE };
254
    EGLSurface eglSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, surface, surfaceAttribs, 0);
255
    checkEglError("eglCreateWindowSurface");
256

    
257
    if (eglSurface == null)
258
      {
259
      throw new RuntimeException("surface was null");
260
      }
261
    return eglSurface;
262
    }
263

    
264
///////////////////////////////////////////////////////////////////////////////////////////////////
265
  /**
266
   * Makes our EGL context current, using the supplied surface for both "draw" and "read".
267
   */
268
  public void makeCurrent(EGLSurface eglSurface)
269
    {
270
    if (mEGLDisplay == EGL14.EGL_NO_DISPLAY)
271
      {
272
      // called makeCurrent() before create?
273
      Log.d(TAG, "NOTE: makeCurrent w/o display");
274
      }
275
    if (!EGL14.eglMakeCurrent(mEGLDisplay, eglSurface, eglSurface, mEGLContext))
276
      {
277
      throw new RuntimeException("eglMakeCurrent failed");
278
      }
279
    }
280

    
281
///////////////////////////////////////////////////////////////////////////////////////////////////
282
  /**
283
   * Makes no context current.
284
   */
285
  public void makeNothingCurrent()
286
    {
287
    if (!EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT))
288
      {
289
      throw new RuntimeException("eglMakeCurrent failed");
290
      }
291
    }
292

    
293
///////////////////////////////////////////////////////////////////////////////////////////////////
294
  /**
295
   * Returns the GLES version this context is configured for (currently 2 or 3).
296
   */
297
  public int getGlVersion()
298
    {
299
    return mGlVersion;
300
    }
301

    
302
///////////////////////////////////////////////////////////////////////////////////////////////////
303
  /**
304
   * Calls eglSwapBuffers.  Use this to "publish" the current frame.
305
   *
306
   * @return false on failure
307
   */
308
  public boolean swapBuffers(EGLSurface eglSurface)
309
    {
310
    return EGL14.eglSwapBuffers(mEGLDisplay, eglSurface);
311
    }
312

    
313
///////////////////////////////////////////////////////////////////////////////////////////////////
314
  /**
315
   * Checks for EGL errors.  Throws an exception if an error has been raised.
316
   */
317
  private void checkEglError(String msg)
318
    {
319
    int error;
320

    
321
    if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS)
322
      {
323
      throw new RuntimeException(msg + ": EGL error: 0x" + Integer.toHexString(error));
324
      }
325
    }
326
  }
327
///////////////////////////////////////////////////////////////////////////////////////////////////
(1-1/5)