Project

General

Profile

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

examples / src / main / java / org / distorted / examples / plainmonalisa / EglCore.java @ ce3787c7

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
    {
52
    this(null, 0);
53
    }
54

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
181
    return configs[0];
182
    }
183

    
184
///////////////////////////////////////////////////////////////////////////////////////////////////
185
  /**
186
   * Discards all resources held by this class, notably the EGL context.  This must be
187
   * called from the thread where the context was created.
188
   * <p>
189
   * On completion, no context will be current.
190
   */
191
  public void release()
192
    {
193
    if (mEGLDisplay != EGL14.EGL_NO_DISPLAY)
194
      {
195
      EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
196
      EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
197
      EGL14.eglReleaseThread();
198
      EGL14.eglTerminate(mEGLDisplay);
199
      }
200

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

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

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

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

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

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

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

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

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

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

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

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