react-native-transparent-video-player 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1825 @@
1
+ /*
2
+ * Copyright 2014 Google Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ package com.alphamovie.lib;
18
+
19
+ import android.content.Context;
20
+ import android.graphics.SurfaceTexture;
21
+ import android.opengl.GLDebugHelper;
22
+ import android.util.AttributeSet;
23
+ import android.util.Log;
24
+ import android.view.TextureView;
25
+ import android.view.View;
26
+
27
+ import java.io.Writer;
28
+ import java.lang.ref.WeakReference;
29
+ import java.util.ArrayList;
30
+
31
+ import javax.microedition.khronos.egl.EGL10;
32
+ import javax.microedition.khronos.egl.EGL11;
33
+ import javax.microedition.khronos.egl.EGLConfig;
34
+ import javax.microedition.khronos.egl.EGLContext;
35
+ import javax.microedition.khronos.egl.EGLDisplay;
36
+ import javax.microedition.khronos.egl.EGLSurface;
37
+ import javax.microedition.khronos.opengles.GL;
38
+ import javax.microedition.khronos.opengles.GL10;
39
+
40
+ public class GLTextureView
41
+ extends TextureView
42
+ implements TextureView.SurfaceTextureListener,
43
+ View.OnLayoutChangeListener {
44
+
45
+ private final static String TAG = "GLTextureView";
46
+ private final static boolean LOG_ATTACH_DETACH = true;
47
+ private final static boolean LOG_THREADS = true;
48
+ private final static boolean LOG_PAUSE_RESUME = true;
49
+ private final static boolean LOG_SURFACE = true;
50
+ private final static boolean LOG_RENDERER = true;
51
+ private final static boolean LOG_RENDERER_DRAW_FRAME = false;
52
+ private final static boolean LOG_EGL = true;
53
+ /**
54
+ * The renderer only renders
55
+ * when the surface is created, or when {@link #requestRender} is called.
56
+ *
57
+ * @see #getRenderMode()
58
+ * @see #setRenderMode(int)
59
+ * @see #requestRender()
60
+ */
61
+ public final static int RENDERMODE_WHEN_DIRTY = 0;
62
+ /**
63
+ * The renderer is called
64
+ * continuously to re-render the scene.
65
+ *
66
+ * @see #getRenderMode()
67
+ * @see #setRenderMode(int)
68
+ */
69
+ public final static int RENDERMODE_CONTINUOUSLY = 1;
70
+
71
+ /**
72
+ * Check glError() after every GL call and throw an exception if glError indicates
73
+ * that an error has occurred. This can be used to help track down which OpenGL ES call
74
+ * is causing an error.
75
+ *
76
+ * @see #getDebugFlags
77
+ * @see #setDebugFlags
78
+ */
79
+ public final static int DEBUG_CHECK_GL_ERROR = 1;
80
+
81
+ /**
82
+ * Log GL calls to the system log at "verbose" level with tag "GLTextureView".
83
+ *
84
+ * @see #getDebugFlags
85
+ * @see #setDebugFlags
86
+ */
87
+ public final static int DEBUG_LOG_GL_CALLS = 2;
88
+
89
+ /**
90
+ * Standard View constructor. In order to render something, you
91
+ * must call {@link #setRenderer} to register a renderer.
92
+ */
93
+ public GLTextureView(Context context) {
94
+ super(context);
95
+ init();
96
+ }
97
+
98
+ /**
99
+ * Standard View constructor. In order to render something, you
100
+ * must call {@link #setRenderer} to register a renderer.
101
+ */
102
+ public GLTextureView(Context context, AttributeSet attrs) {
103
+ super(context, attrs);
104
+ init();
105
+ }
106
+
107
+ @Override
108
+ protected void finalize() throws Throwable {
109
+ try {
110
+ if (mGLThread != null) {
111
+ // GLThread may still be running if this view was never
112
+ // attached to a window.
113
+ mGLThread.requestExitAndWait();
114
+ }
115
+ } finally {
116
+ super.finalize();
117
+ }
118
+ }
119
+
120
+ private void init() {
121
+ setSurfaceTextureListener(this);
122
+ }
123
+
124
+ /**
125
+ * Set the glWrapper. If the glWrapper is not null, its
126
+ * {@link GLWrapper#wrap(javax.microedition.khronos.opengles.GL)} method is called
127
+ * whenever a surface is created. A GLWrapper can be used to wrap
128
+ * the GL object that's passed to the renderer. Wrapping a GL
129
+ * object enables examining and modifying the behavior of the
130
+ * GL calls made by the renderer.
131
+ * <p>
132
+ * Wrapping is typically used for debugging purposes.
133
+ * <p>
134
+ * The default value is null.
135
+ * @param glWrapper the new GLWrapper
136
+ */
137
+ public void setGLWrapper(GLWrapper glWrapper) {
138
+ mGLWrapper = glWrapper;
139
+ }
140
+
141
+ /**
142
+ * Set the debug flags to a new value. The value is
143
+ * constructed by OR-together zero or more
144
+ * of the DEBUG_CHECK_* constants. The debug flags take effect
145
+ * whenever a surface is created. The default value is zero.
146
+ * @param debugFlags the new debug flags
147
+ * @see #DEBUG_CHECK_GL_ERROR
148
+ * @see #DEBUG_LOG_GL_CALLS
149
+ */
150
+ public void setDebugFlags(int debugFlags) {
151
+ mDebugFlags = debugFlags;
152
+ }
153
+
154
+ /**
155
+ * Get the current value of the debug flags.
156
+ * @return the current value of the debug flags.
157
+ */
158
+ public int getDebugFlags() {
159
+ return mDebugFlags;
160
+ }
161
+
162
+ /**
163
+ * Control whether the EGL context is preserved when the GLTextureView is paused and
164
+ * resumed.
165
+ * <p>
166
+ * If set to true, then the EGL context may be preserved when the GLTextureView is paused.
167
+ * Whether the EGL context is actually preserved or not depends upon whether the
168
+ * Android device that the program is running on can support an arbitrary number of EGL
169
+ * contexts or not. Devices that can only support a limited number of EGL contexts must
170
+ * release the EGL context in order to allow multiple applications to share the GPU.
171
+ * <p>
172
+ * If set to false, the EGL context will be released when the GLTextureView is paused,
173
+ * and recreated when the GLTextureView is resumed.
174
+ * <p>
175
+ *
176
+ * The default is false.
177
+ *
178
+ * @param preserveOnPause preserve the EGL context when paused
179
+ */
180
+ public void setPreserveEGLContextOnPause(boolean preserveOnPause) {
181
+ mPreserveEGLContextOnPause = preserveOnPause;
182
+ }
183
+
184
+ /**
185
+ * @return true if the EGL context will be preserved when paused
186
+ */
187
+ public boolean getPreserveEGLContextOnPause() {
188
+ return mPreserveEGLContextOnPause;
189
+ }
190
+
191
+ /**
192
+ * Set the renderer associated with this view. Also starts the thread that
193
+ * will call the renderer, which in turn causes the rendering to start.
194
+ * <p>This method should be called once and only once in the life-cycle of
195
+ * a GLTextureView.
196
+ * <p>The following GLTextureView methods can only be called <em>before</em>
197
+ * setRenderer is called:
198
+ * <ul>
199
+ * <li>{@link #setEGLConfigChooser(boolean)}
200
+ * <li>{@link #setEGLConfigChooser(EGLConfigChooser)}
201
+ * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)}
202
+ * </ul>
203
+ * <p>
204
+ * The following GLTextureView methods can only be called <em>after</em>
205
+ * setRenderer is called:
206
+ * <ul>
207
+ * <li>{@link #getRenderMode()}
208
+ * <li>{@link #onPause()}
209
+ * <li>{@link #onResume()}
210
+ * <li>{@link #queueEvent(Runnable)}
211
+ * <li>{@link #requestRender()}
212
+ * <li>{@link #setRenderMode(int)}
213
+ * </ul>
214
+ *
215
+ * @param renderer the renderer to use to perform OpenGL drawing.
216
+ */
217
+ public void setRenderer(Renderer renderer) {
218
+ checkRenderThreadState();
219
+ if (mEGLConfigChooser == null) {
220
+ mEGLConfigChooser = new SimpleEGLConfigChooser(true);
221
+ }
222
+ if (mEGLContextFactory == null) {
223
+ mEGLContextFactory = new DefaultContextFactory();
224
+ }
225
+ if (mEGLWindowSurfaceFactory == null) {
226
+ mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
227
+ }
228
+ mRenderer = renderer;
229
+ mGLThread = new GLThread(mThisWeakRef);
230
+ mGLThread.start();
231
+ }
232
+
233
+ /**
234
+ * Install a custom EGLContextFactory.
235
+ * <p>If this method is
236
+ * called, it must be called before {@link #setRenderer(Renderer)}
237
+ * is called.
238
+ * <p>
239
+ * If this method is not called, then by default
240
+ * a context will be created with no shared context and
241
+ * with a null attribute list.
242
+ */
243
+ public void setEGLContextFactory(EGLContextFactory factory) {
244
+ checkRenderThreadState();
245
+ mEGLContextFactory = factory;
246
+ }
247
+
248
+ /**
249
+ * Install a custom EGLWindowSurfaceFactory.
250
+ * <p>If this method is
251
+ * called, it must be called before {@link #setRenderer(Renderer)}
252
+ * is called.
253
+ * <p>
254
+ * If this method is not called, then by default
255
+ * a window surface will be created with a null attribute list.
256
+ */
257
+ public void setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory factory) {
258
+ checkRenderThreadState();
259
+ mEGLWindowSurfaceFactory = factory;
260
+ }
261
+
262
+ /**
263
+ * Install a custom EGLConfigChooser.
264
+ * <p>If this method is
265
+ * called, it must be called before {@link #setRenderer(Renderer)}
266
+ * is called.
267
+ * <p>
268
+ * If no setEGLConfigChooser method is called, then by default the
269
+ * view will choose an EGLConfig that is compatible with the current
270
+ * android.view.Surface, with a depth buffer depth of
271
+ * at least 16 bits.
272
+ * @param configChooser
273
+ */
274
+ public void setEGLConfigChooser(EGLConfigChooser configChooser) {
275
+ checkRenderThreadState();
276
+ mEGLConfigChooser = configChooser;
277
+ }
278
+
279
+ /**
280
+ * Install a config chooser which will choose a config
281
+ * as close to 16-bit RGB as possible, with or without an optional depth
282
+ * buffer as close to 16-bits as possible.
283
+ * <p>If this method is
284
+ * called, it must be called before {@link #setRenderer(Renderer)}
285
+ * is called.
286
+ * <p>
287
+ * If no setEGLConfigChooser method is called, then by default the
288
+ * view will choose an RGB_888 surface with a depth buffer depth of
289
+ * at least 16 bits.
290
+ *
291
+ * @param needDepth
292
+ */
293
+ public void setEGLConfigChooser(boolean needDepth) {
294
+ setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth));
295
+ }
296
+
297
+ /**
298
+ * Install a config chooser which will choose a config
299
+ * with at least the specified depthSize and stencilSize,
300
+ * and exactly the specified redSize, greenSize, blueSize and alphaSize.
301
+ * <p>If this method is
302
+ * called, it must be called before {@link #setRenderer(Renderer)}
303
+ * is called.
304
+ * <p>
305
+ * If no setEGLConfigChooser method is called, then by default the
306
+ * view will choose an RGB_888 surface with a depth buffer depth of
307
+ * at least 16 bits.
308
+ *
309
+ */
310
+ public void setEGLConfigChooser(int redSize, int greenSize, int blueSize,
311
+ int alphaSize, int depthSize, int stencilSize) {
312
+ setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize,
313
+ blueSize, alphaSize, depthSize, stencilSize));
314
+ }
315
+
316
+ /**
317
+ * Inform the default EGLContextFactory and default EGLConfigChooser
318
+ * which EGLContext client version to pick.
319
+ * <p>Use this method to create an OpenGL ES 2.0-compatible context.
320
+ * Example:
321
+ * <pre class="prettyprint">
322
+ * public MyView(Context context) {
323
+ * super(context);
324
+ * setEGLContextClientVersion(2); // Pick an OpenGL ES 2.0 context.
325
+ * setRenderer(new MyRenderer());
326
+ * }
327
+ * </pre>
328
+ * <p>Note: Activities which require OpenGL ES 2.0 should indicate this by
329
+ * setting @lt;uses-feature android:glEsVersion="0x00020000" /> in the activity's
330
+ * AndroidManifest.xml file.
331
+ * <p>If this method is called, it must be called before {@link #setRenderer(Renderer)}
332
+ * is called.
333
+ * <p>This method only affects the behavior of the default EGLContexFactory and the
334
+ * default EGLConfigChooser. If
335
+ * {@link #setEGLContextFactory(EGLContextFactory)} has been called, then the supplied
336
+ * EGLContextFactory is responsible for creating an OpenGL ES 2.0-compatible context.
337
+ * If
338
+ * {@link #setEGLConfigChooser(EGLConfigChooser)} has been called, then the supplied
339
+ * EGLConfigChooser is responsible for choosing an OpenGL ES 2.0-compatible config.
340
+ * @param version The EGLContext client version to choose. Use 2 for OpenGL ES 2.0
341
+ */
342
+ public void setEGLContextClientVersion(int version) {
343
+ checkRenderThreadState();
344
+ mEGLContextClientVersion = version;
345
+ }
346
+
347
+ /**
348
+ * Set the rendering mode. When renderMode is
349
+ * RENDERMODE_CONTINUOUSLY, the renderer is called
350
+ * repeatedly to re-render the scene. When renderMode
351
+ * is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface
352
+ * is created, or when {@link #requestRender} is called. Defaults to RENDERMODE_CONTINUOUSLY.
353
+ * <p>
354
+ * Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system performance
355
+ * by allowing the GPU and CPU to idle when the view does not need to be updated.
356
+ * <p>
357
+ * This method can only be called after {@link #setRenderer(Renderer)}
358
+ *
359
+ * @param renderMode one of the RENDERMODE_X constants
360
+ * @see #RENDERMODE_CONTINUOUSLY
361
+ * @see #RENDERMODE_WHEN_DIRTY
362
+ */
363
+ public void setRenderMode(int renderMode) {
364
+ mGLThread.setRenderMode(renderMode);
365
+ }
366
+
367
+ /**
368
+ * Get the current rendering mode. May be called
369
+ * from any thread. Must not be called before a renderer has been set.
370
+ * @return the current rendering mode.
371
+ * @see #RENDERMODE_CONTINUOUSLY
372
+ * @see #RENDERMODE_WHEN_DIRTY
373
+ */
374
+ public int getRenderMode() {
375
+ return mGLThread.getRenderMode();
376
+ }
377
+
378
+ /**
379
+ * Request that the renderer render a frame.
380
+ * This method is typically used when the render mode has been set to
381
+ * {@link #RENDERMODE_WHEN_DIRTY}, so that frames are only rendered on demand.
382
+ * May be called
383
+ * from any thread. Must not be called before a renderer has been set.
384
+ */
385
+ public void requestRender() {
386
+ mGLThread.requestRender();
387
+ }
388
+
389
+ /**
390
+ * This method is part of the SurfaceHolder.Callback interface, and is
391
+ * not normally called or subclassed by clients of GLTextureView.
392
+ */
393
+ public void surfaceCreated(SurfaceTexture texture) {
394
+ mGLThread.surfaceCreated();
395
+ }
396
+
397
+ /**
398
+ * This method is part of the SurfaceHolder.Callback interface, and is
399
+ * not normally called or subclassed by clients of GLTextureView.
400
+ */
401
+ public void surfaceDestroyed(SurfaceTexture texture) {
402
+ // Surface will be destroyed when we return
403
+ mGLThread.surfaceDestroyed();
404
+ }
405
+
406
+ /**
407
+ * This method is part of the SurfaceHolder.Callback interface, and is
408
+ * not normally called or subclassed by clients of GLTextureView.
409
+ */
410
+ public void surfaceChanged(SurfaceTexture texture, int format, int w, int h) {
411
+ mGLThread.onWindowResize(w, h);
412
+ }
413
+
414
+ /**
415
+ * Inform the view that the activity is paused. The owner of this view must
416
+ * call this method when the activity is paused. Calling this method will
417
+ * pause the rendering thread.
418
+ * Must not be called before a renderer has been set.
419
+ */
420
+ public void onPause() {
421
+ mGLThread.onPause();
422
+ }
423
+
424
+ /**
425
+ * Inform the view that the activity is resumed. The owner of this view must
426
+ * call this method when the activity is resumed. Calling this method will
427
+ * recreate the OpenGL display and resume the rendering
428
+ * thread.
429
+ * Must not be called before a renderer has been set.
430
+ */
431
+ public void onResume() {
432
+ mGLThread.onResume();
433
+ }
434
+
435
+ /**
436
+ * Queue a runnable to be run on the GL rendering thread. This can be used
437
+ * to communicate with the Renderer on the rendering thread.
438
+ * Must not be called before a renderer has been set.
439
+ * @param r the runnable to be run on the GL rendering thread.
440
+ */
441
+ public void queueEvent(Runnable r) {
442
+ mGLThread.queueEvent(r);
443
+ }
444
+
445
+ /**
446
+ * This method is used as part of the View class and is not normally
447
+ * called or subclassed by clients of GLTextureView.
448
+ */
449
+ @Override
450
+ protected void onAttachedToWindow() {
451
+ super.onAttachedToWindow();
452
+ if (LOG_ATTACH_DETACH) {
453
+ Log.d(TAG, "onAttachedToWindow reattach =" + mDetached);
454
+ }
455
+ if (mDetached && (mRenderer != null)) {
456
+ int renderMode = RENDERMODE_CONTINUOUSLY;
457
+ if (mGLThread != null) {
458
+ renderMode = mGLThread.getRenderMode();
459
+ }
460
+ mGLThread = new GLThread(mThisWeakRef);
461
+ if (renderMode != RENDERMODE_CONTINUOUSLY) {
462
+ mGLThread.setRenderMode(renderMode);
463
+ }
464
+ mGLThread.start();
465
+ }
466
+ mDetached = false;
467
+ }
468
+
469
+ /**
470
+ * This method is used as part of the View class and is not normally
471
+ * called or subclassed by clients of GLTextureView.
472
+ * Must not be called before a renderer has been set.
473
+ */
474
+ @Override
475
+ protected void onDetachedFromWindow() {
476
+ if (LOG_ATTACH_DETACH) {
477
+ Log.d(TAG, "onDetachedFromWindow");
478
+ }
479
+ if (mGLThread != null) {
480
+ mGLThread.requestExitAndWait();
481
+ }
482
+ mDetached = true;
483
+ super.onDetachedFromWindow();
484
+ }
485
+
486
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
487
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
488
+ surfaceChanged(getSurfaceTexture(), 0, right - left, bottom - top);
489
+ }
490
+
491
+ public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
492
+ surfaceCreated(surface);
493
+ surfaceChanged(surface, 0, width, height);
494
+ }
495
+
496
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
497
+ surfaceChanged(surface, 0, width, height);
498
+ }
499
+
500
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
501
+ surfaceDestroyed(surface);
502
+ return true;
503
+ }
504
+
505
+ public void onSurfaceTextureUpdated(SurfaceTexture surface) {
506
+ requestRender();
507
+ }
508
+
509
+ // ----------------------------------------------------------------------
510
+
511
+ /**
512
+ * An interface used to wrap a GL interface.
513
+ * <p>Typically
514
+ * used for implementing debugging and tracing on top of the default
515
+ * GL interface. You would typically use this by creating your own class
516
+ * that implemented all the GL methods by delegating to another GL instance.
517
+ * Then you could add your own behavior before or after calling the
518
+ * delegate. All the GLWrapper would do was instantiate and return the
519
+ * wrapper GL instance:
520
+ * <pre class="prettyprint">
521
+ * class MyGLWrapper implements GLWrapper {
522
+ * GL wrap(GL gl) {
523
+ * return new MyGLImplementation(gl);
524
+ * }
525
+ * static class MyGLImplementation implements GL,GL10,GL11,... {
526
+ * ...
527
+ * }
528
+ * }
529
+ * </pre>
530
+ * @see #setGLWrapper(GLWrapper)
531
+ */
532
+ public interface GLWrapper {
533
+ /**
534
+ * Wraps a gl interface in another gl interface.
535
+ * @param gl a GL interface that is to be wrapped.
536
+ * @return either the input argument or another GL object that wraps the input argument.
537
+ */
538
+ GL wrap(GL gl);
539
+ }
540
+
541
+ /**
542
+ * A generic renderer interface.
543
+ * <p>
544
+ * The renderer is responsible for making OpenGL calls to render a frame.
545
+ * <p>
546
+ * GLTextureView clients typically create their own classes that implement
547
+ * this interface, and then call {@link GLTextureView#setRenderer} to
548
+ * register the renderer with the GLTextureView.
549
+ * <p>
550
+ *
551
+ * <div class="special reference">
552
+ * <h3>Developer Guides</h3>
553
+ * <p>For more information about how to use OpenGL, read the
554
+ * <a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a> developer guide.</p>
555
+ * </div>
556
+ *
557
+ * <h3>Threading</h3>
558
+ * The renderer will be called on a separate thread, so that rendering
559
+ * performance is decoupled from the UI thread. Clients typically need to
560
+ * communicate with the renderer from the UI thread, because that's where
561
+ * input events are received. Clients can communicate using any of the
562
+ * standard Java techniques for cross-thread communication, or they can
563
+ * use the {@link GLTextureView#queueEvent(Runnable)} convenience method.
564
+ * <p>
565
+ * <h3>EGL Context Lost</h3>
566
+ * There are situations where the EGL rendering context will be lost. This
567
+ * typically happens when device wakes up after going to sleep. When
568
+ * the EGL context is lost, all OpenGL resources (such as textures) that are
569
+ * associated with that context will be automatically deleted. In order to
570
+ * keep rendering correctly, a renderer must recreate any lost resources
571
+ * that it still needs. The {@link #onSurfaceCreated(javax.microedition.khronos.opengles.GL10, javax.microedition.khronos.egl.EGLConfig)} method
572
+ * is a convenient place to do this.
573
+ *
574
+ *
575
+ * @see #setRenderer(Renderer)
576
+ */
577
+ public interface Renderer {
578
+ /**
579
+ * Called when the surface is created or recreated.
580
+ * <p>
581
+ * Called when the rendering thread
582
+ * starts and whenever the EGL context is lost. The EGL context will typically
583
+ * be lost when the Android device awakes after going to sleep.
584
+ * <p>
585
+ * Since this method is called at the beginning of rendering, as well as
586
+ * every time the EGL context is lost, this method is a convenient place to put
587
+ * code to create resources that need to be created when the rendering
588
+ * starts, and that need to be recreated when the EGL context is lost.
589
+ * Textures are an example of a resource that you might want to create
590
+ * here.
591
+ * <p>
592
+ * Note that when the EGL context is lost, all OpenGL resources associated
593
+ * with that context will be automatically deleted. You do not need to call
594
+ * the corresponding "glDelete" methods such as glDeleteTextures to
595
+ * manually delete these lost resources.
596
+ * <p>
597
+ * @param gl the GL interface. Use <code>instanceof</code> to
598
+ * test if the interface supports GL11 or higher interfaces.
599
+ * @param config the EGLConfig of the created surface. Can be used
600
+ * to create matching pbuffers.
601
+ */
602
+ void onSurfaceCreated(GL10 gl, EGLConfig config);
603
+
604
+ /**
605
+ * Called when the surface changed size.
606
+ * <p>
607
+ * Called after the surface is created and whenever
608
+ * the OpenGL ES surface size changes.
609
+ * <p>
610
+ * Typically you will set your viewport here. If your camera
611
+ * is fixed then you could also set your projection matrix here:
612
+ * <pre class="prettyprint">
613
+ * void onSurfaceChanged(GL10 gl, int width, int height) {
614
+ * gl.glViewport(0, 0, width, height);
615
+ * // for a fixed camera, set the projection too
616
+ * float ratio = (float) width / height;
617
+ * gl.glMatrixMode(GL10.GL_PROJECTION);
618
+ * gl.glLoadIdentity();
619
+ * gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
620
+ * }
621
+ * </pre>
622
+ * @param gl the GL interface. Use <code>instanceof</code> to
623
+ * test if the interface supports GL11 or higher interfaces.
624
+ * @param width
625
+ * @param height
626
+ */
627
+ void onSurfaceChanged(GL10 gl, int width, int height);
628
+
629
+ /**
630
+ * Called to draw the current frame.
631
+ * <p>
632
+ * This method is responsible for drawing the current frame.
633
+ * <p>
634
+ * The implementation of this method typically looks like this:
635
+ * <pre class="prettyprint">
636
+ * void onDrawFrame(GL10 gl) {
637
+ * gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
638
+ * //... other gl calls to render the scene ...
639
+ * }
640
+ * </pre>
641
+ * @param gl the GL interface. Use <code>instanceof</code> to
642
+ * test if the interface supports GL11 or higher interfaces.
643
+ */
644
+ void onDrawFrame(GL10 gl);
645
+
646
+ void onSurfaceDestroyed(GL10 gl);
647
+ }
648
+
649
+ /**
650
+ * An interface for customizing the eglCreateContext and eglDestroyContext calls.
651
+ * <p>
652
+ * This interface must be implemented by clients wishing to call
653
+ * {@link GLTextureView#setEGLContextFactory(EGLContextFactory)}
654
+ */
655
+ public interface EGLContextFactory {
656
+ EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig);
657
+ void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context);
658
+ }
659
+
660
+ private class DefaultContextFactory implements EGLContextFactory {
661
+ private int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
662
+
663
+ public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {
664
+ int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion,
665
+ EGL10.EGL_NONE };
666
+
667
+ return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT,
668
+ mEGLContextClientVersion != 0 ? attrib_list : null);
669
+ }
670
+
671
+ public void destroyContext(EGL10 egl, EGLDisplay display,
672
+ EGLContext context) {
673
+ if (!egl.eglDestroyContext(display, context)) {
674
+ Log.e("DefaultContextFactory", "display:" + display + " context: " + context);
675
+ if (LOG_THREADS) {
676
+ Log.i("DefaultContextFactory", "tid=" + Thread.currentThread().getId());
677
+ }
678
+ EglHelper.throwEglException("eglDestroyContex", egl.eglGetError());
679
+ }
680
+ }
681
+ }
682
+
683
+ /**
684
+ * An interface for customizing the eglCreateWindowSurface and eglDestroySurface calls.
685
+ * <p>
686
+ * This interface must be implemented by clients wishing to call
687
+ * {@link GLTextureView#setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory)}
688
+ */
689
+ public interface EGLWindowSurfaceFactory {
690
+ /**
691
+ * @return null if the surface cannot be constructed.
692
+ */
693
+ EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config,
694
+ Object nativeWindow);
695
+ void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface);
696
+ }
697
+
698
+ private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory {
699
+
700
+ public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display,
701
+ EGLConfig config, Object nativeWindow) {
702
+ EGLSurface result = null;
703
+ try {
704
+ result = egl.eglCreateWindowSurface(display, config, nativeWindow, null);
705
+ } catch (IllegalArgumentException e) {
706
+ // This exception indicates that the surface flinger surface
707
+ // is not valid. This can happen if the surface flinger surface has
708
+ // been torn down, but the application has not yet been
709
+ // notified via SurfaceHolder.Callback.surfaceDestroyed.
710
+ // In theory the application should be notified first,
711
+ // but in practice sometimes it is not. See b/4588890
712
+ Log.e(TAG, "eglCreateWindowSurface", e);
713
+ }
714
+ return result;
715
+ }
716
+
717
+ public void destroySurface(EGL10 egl, EGLDisplay display,
718
+ EGLSurface surface) {
719
+ egl.eglDestroySurface(display, surface);
720
+ }
721
+ }
722
+
723
+ /**
724
+ * An interface for choosing an EGLConfig configuration from a list of
725
+ * potential configurations.
726
+ * <p>
727
+ * This interface must be implemented by clients wishing to call
728
+ * {@link GLTextureView#setEGLConfigChooser(EGLConfigChooser)}
729
+ */
730
+ public interface EGLConfigChooser {
731
+ /**
732
+ * Choose a configuration from the list. Implementors typically
733
+ * implement this method by calling
734
+ * {@link EGL10#eglChooseConfig} and iterating through the results. Please consult the
735
+ * EGL specification available from The Khronos Group to learn how to call eglChooseConfig.
736
+ * @param egl the EGL10 for the current display.
737
+ * @param display the current display.
738
+ * @return the chosen configuration.
739
+ */
740
+ EGLConfig chooseConfig(EGL10 egl, EGLDisplay display);
741
+ }
742
+
743
+ private abstract class BaseConfigChooser
744
+ implements EGLConfigChooser {
745
+ public BaseConfigChooser(int[] configSpec) {
746
+ mConfigSpec = filterConfigSpec(configSpec);
747
+ }
748
+
749
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
750
+ int[] num_config = new int[1];
751
+ if (!egl.eglChooseConfig(display, mConfigSpec, null, 0,
752
+ num_config)) {
753
+ throw new IllegalArgumentException("eglChooseConfig failed");
754
+ }
755
+
756
+ int numConfigs = num_config[0];
757
+
758
+ if (numConfigs <= 0) {
759
+ throw new IllegalArgumentException(
760
+ "No configs match configSpec");
761
+ }
762
+
763
+ EGLConfig[] configs = new EGLConfig[numConfigs];
764
+ if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs,
765
+ num_config)) {
766
+ throw new IllegalArgumentException("eglChooseConfig#2 failed");
767
+ }
768
+ EGLConfig config = chooseConfig(egl, display, configs);
769
+ if (config == null) {
770
+ throw new IllegalArgumentException("No config chosen");
771
+ }
772
+ return config;
773
+ }
774
+
775
+ abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
776
+ EGLConfig[] configs);
777
+
778
+ protected int[] mConfigSpec;
779
+
780
+ private int[] filterConfigSpec(int[] configSpec) {
781
+ if (mEGLContextClientVersion != 2) {
782
+ return configSpec;
783
+ }
784
+ /* We know none of the subclasses define EGL_RENDERABLE_TYPE.
785
+ * And we know the configSpec is well formed.
786
+ */
787
+ int len = configSpec.length;
788
+ int[] newConfigSpec = new int[len + 2];
789
+ System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1);
790
+ newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE;
791
+ newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
792
+ newConfigSpec[len+1] = EGL10.EGL_NONE;
793
+ return newConfigSpec;
794
+ }
795
+ }
796
+
797
+ /**
798
+ * Choose a configuration with exactly the specified r,g,b,a sizes,
799
+ * and at least the specified depth and stencil sizes.
800
+ */
801
+ private class ComponentSizeChooser extends BaseConfigChooser {
802
+ public ComponentSizeChooser(int redSize, int greenSize, int blueSize,
803
+ int alphaSize, int depthSize, int stencilSize) {
804
+ super(new int[] {
805
+ EGL10.EGL_RED_SIZE, redSize,
806
+ EGL10.EGL_GREEN_SIZE, greenSize,
807
+ EGL10.EGL_BLUE_SIZE, blueSize,
808
+ EGL10.EGL_ALPHA_SIZE, alphaSize,
809
+ EGL10.EGL_DEPTH_SIZE, depthSize,
810
+ EGL10.EGL_STENCIL_SIZE, stencilSize,
811
+ EGL10.EGL_NONE});
812
+ mValue = new int[1];
813
+ mRedSize = redSize;
814
+ mGreenSize = greenSize;
815
+ mBlueSize = blueSize;
816
+ mAlphaSize = alphaSize;
817
+ mDepthSize = depthSize;
818
+ mStencilSize = stencilSize;
819
+ }
820
+
821
+ @Override
822
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
823
+ EGLConfig[] configs) {
824
+ for (EGLConfig config : configs) {
825
+ int d = findConfigAttrib(egl, display, config,
826
+ EGL10.EGL_DEPTH_SIZE, 0);
827
+ int s = findConfigAttrib(egl, display, config,
828
+ EGL10.EGL_STENCIL_SIZE, 0);
829
+ if ((d >= mDepthSize) && (s >= mStencilSize)) {
830
+ int r = findConfigAttrib(egl, display, config,
831
+ EGL10.EGL_RED_SIZE, 0);
832
+ int g = findConfigAttrib(egl, display, config,
833
+ EGL10.EGL_GREEN_SIZE, 0);
834
+ int b = findConfigAttrib(egl, display, config,
835
+ EGL10.EGL_BLUE_SIZE, 0);
836
+ int a = findConfigAttrib(egl, display, config,
837
+ EGL10.EGL_ALPHA_SIZE, 0);
838
+ if ((r == mRedSize) && (g == mGreenSize)
839
+ && (b == mBlueSize) && (a == mAlphaSize)) {
840
+ return config;
841
+ }
842
+ }
843
+ }
844
+ return null;
845
+ }
846
+
847
+ private int findConfigAttrib(EGL10 egl, EGLDisplay display,
848
+ EGLConfig config, int attribute, int defaultValue) {
849
+
850
+ if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
851
+ return mValue[0];
852
+ }
853
+ return defaultValue;
854
+ }
855
+
856
+ private int[] mValue;
857
+ // Subclasses can adjust these values:
858
+ protected int mRedSize;
859
+ protected int mGreenSize;
860
+ protected int mBlueSize;
861
+ protected int mAlphaSize;
862
+ protected int mDepthSize;
863
+ protected int mStencilSize;
864
+ }
865
+
866
+ /**
867
+ * This class will choose a RGB_888 surface with
868
+ * or without a depth buffer.
869
+ *
870
+ */
871
+ private class SimpleEGLConfigChooser extends ComponentSizeChooser {
872
+ public SimpleEGLConfigChooser(boolean withDepthBuffer) {
873
+ super(8, 8, 8, 0, withDepthBuffer ? 16 : 0, 0);
874
+ }
875
+ }
876
+
877
+ /**
878
+ * An EGL helper class.
879
+ */
880
+
881
+ private static class EglHelper {
882
+ public EglHelper(WeakReference<GLTextureView> glSurfaceViewWeakRef) {
883
+ mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
884
+ }
885
+
886
+ /**
887
+ * Initialize EGL for a given configuration spec.
888
+ * @param
889
+ */
890
+ public void start() {
891
+ if (LOG_EGL) {
892
+ Log.w("EglHelper", "start() tid=" + Thread.currentThread().getId());
893
+ }
894
+ /*
895
+ * Get an EGL instance
896
+ */
897
+ mEgl = (EGL10) EGLContext.getEGL();
898
+
899
+ /*
900
+ * Get to the default display.
901
+ */
902
+ mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
903
+
904
+ if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
905
+ throw new RuntimeException("eglGetDisplay failed");
906
+ }
907
+
908
+ /*
909
+ * We can now initialize EGL for that display
910
+ */
911
+ int[] version = new int[2];
912
+ if(!mEgl.eglInitialize(mEglDisplay, version)) {
913
+ throw new RuntimeException("eglInitialize failed");
914
+ }
915
+ GLTextureView view = mGLSurfaceViewWeakRef.get();
916
+ if (view == null) {
917
+ mEglConfig = null;
918
+ mEglContext = null;
919
+ } else {
920
+ mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
921
+
922
+ /*
923
+ * Create an EGL context. We want to do this as rarely as we can, because an
924
+ * EGL context is a somewhat heavy object.
925
+ */
926
+ mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
927
+ }
928
+ if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
929
+ mEglContext = null;
930
+ throwEglException("createContext");
931
+ }
932
+ if (LOG_EGL) {
933
+ Log.w("EglHelper", "createContext " + mEglContext + " tid=" + Thread.currentThread().getId());
934
+ }
935
+
936
+ mEglSurface = null;
937
+ }
938
+
939
+ /**
940
+ * Create an egl surface for the current SurfaceHolder surface. If a surface
941
+ * already exists, destroy it before creating the new surface.
942
+ *
943
+ * @return true if the surface was created successfully.
944
+ */
945
+ public boolean createSurface() {
946
+ if (LOG_EGL) {
947
+ Log.w("EglHelper", "createSurface() tid=" + Thread.currentThread().getId());
948
+ }
949
+ /*
950
+ * Check preconditions.
951
+ */
952
+ if (mEgl == null) {
953
+ throw new RuntimeException("egl not initialized");
954
+ }
955
+ if (mEglDisplay == null) {
956
+ throw new RuntimeException("eglDisplay not initialized");
957
+ }
958
+ if (mEglConfig == null) {
959
+ throw new RuntimeException("mEglConfig not initialized");
960
+ }
961
+
962
+ /*
963
+ * The window size has changed, so we need to create a new
964
+ * surface.
965
+ */
966
+ destroySurfaceImp();
967
+
968
+ /*
969
+ * Create an EGL surface we can render into.
970
+ */
971
+ GLTextureView view = mGLSurfaceViewWeakRef.get();
972
+ if (view != null) {
973
+ mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl,
974
+ mEglDisplay, mEglConfig, view.getSurfaceTexture());
975
+ } else {
976
+ mEglSurface = null;
977
+ }
978
+
979
+ if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
980
+ int error = mEgl.eglGetError();
981
+ if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
982
+ Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
983
+ }
984
+ return false;
985
+ }
986
+
987
+ /*
988
+ * Before we can issue GL commands, we need to make sure
989
+ * the context is current and bound to a surface.
990
+ */
991
+ if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
992
+ /*
993
+ * Could not make the context current, probably because the underlying
994
+ * SurfaceView surface has been destroyed.
995
+ */
996
+ logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError());
997
+ return false;
998
+ }
999
+
1000
+ return true;
1001
+ }
1002
+
1003
+ /**
1004
+ * Create a GL object for the current EGL context.
1005
+ * @return
1006
+ */
1007
+ GL createGL() {
1008
+
1009
+ GL gl = mEglContext.getGL();
1010
+ GLTextureView view = mGLSurfaceViewWeakRef.get();
1011
+ if (view != null) {
1012
+ if (view.mGLWrapper != null) {
1013
+ gl = view.mGLWrapper.wrap(gl);
1014
+ }
1015
+
1016
+ if ((view.mDebugFlags & (DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS)) != 0) {
1017
+ int configFlags = 0;
1018
+ Writer log = null;
1019
+ if ((view.mDebugFlags & DEBUG_CHECK_GL_ERROR) != 0) {
1020
+ configFlags |= GLDebugHelper.CONFIG_CHECK_GL_ERROR;
1021
+ }
1022
+ if ((view.mDebugFlags & DEBUG_LOG_GL_CALLS) != 0) {
1023
+ log = new LogWriter();
1024
+ }
1025
+ gl = GLDebugHelper.wrap(gl, configFlags, log);
1026
+ }
1027
+ }
1028
+ return gl;
1029
+ }
1030
+
1031
+ /**
1032
+ * Display the current render surface.
1033
+ * @return the EGL error code from eglSwapBuffers.
1034
+ */
1035
+ public int swap() {
1036
+ if (! mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
1037
+ return mEgl.eglGetError();
1038
+ }
1039
+ return EGL10.EGL_SUCCESS;
1040
+ }
1041
+
1042
+ public void destroySurface() {
1043
+ if (LOG_EGL) {
1044
+ Log.w("EglHelper", "destroySurface() tid=" + Thread.currentThread().getId());
1045
+ }
1046
+ destroySurfaceImp();
1047
+ }
1048
+
1049
+ private void destroySurfaceImp() {
1050
+ if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
1051
+ mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
1052
+ EGL10.EGL_NO_SURFACE,
1053
+ EGL10.EGL_NO_CONTEXT);
1054
+ GLTextureView view = mGLSurfaceViewWeakRef.get();
1055
+ if (view != null) {
1056
+ view.mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
1057
+ }
1058
+ mEglSurface = null;
1059
+ }
1060
+ }
1061
+
1062
+ public void finish() {
1063
+ if (LOG_EGL) {
1064
+ Log.w("EglHelper", "finish() tid=" + Thread.currentThread().getId());
1065
+ }
1066
+ if (mEglContext != null) {
1067
+ GLTextureView view = mGLSurfaceViewWeakRef.get();
1068
+ if (view != null) {
1069
+ view.mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext);
1070
+ }
1071
+ mEglContext = null;
1072
+ }
1073
+ if (mEglDisplay != null) {
1074
+ mEgl.eglTerminate(mEglDisplay);
1075
+ mEglDisplay = null;
1076
+ }
1077
+ }
1078
+
1079
+ private void throwEglException(String function) {
1080
+ throwEglException(function, mEgl.eglGetError());
1081
+ }
1082
+
1083
+ public static void throwEglException(String function, int error) {
1084
+ String message = formatEglError(function, error);
1085
+ if (LOG_THREADS) {
1086
+ Log.e("EglHelper", "throwEglException tid=" + Thread.currentThread().getId() + " "
1087
+ + message);
1088
+ }
1089
+ throw new RuntimeException(message);
1090
+ }
1091
+
1092
+ public static void logEglErrorAsWarning(String tag, String function, int error) {
1093
+ Log.w(tag, formatEglError(function, error));
1094
+ }
1095
+
1096
+ public static String formatEglError(String function, int error) {
1097
+ return function + " failed: " + error;
1098
+ }
1099
+
1100
+ private WeakReference<GLTextureView> mGLSurfaceViewWeakRef;
1101
+ EGL10 mEgl;
1102
+ EGLDisplay mEglDisplay;
1103
+ EGLSurface mEglSurface;
1104
+ EGLConfig mEglConfig;
1105
+ EGLContext mEglContext;
1106
+
1107
+ }
1108
+
1109
+ /**
1110
+ * A generic GL Thread. Takes care of initializing EGL and GL. Delegates
1111
+ * to a Renderer instance to do the actual drawing. Can be configured to
1112
+ * render continuously or on request.
1113
+ *
1114
+ * All potentially blocking synchronization is done through the
1115
+ * sGLThreadManager object. This avoids multiple-lock ordering issues.
1116
+ *
1117
+ */
1118
+ static class GLThread extends Thread {
1119
+ GLThread(WeakReference<GLTextureView> glSurfaceViewWeakRef) {
1120
+ super();
1121
+ mWidth = 0;
1122
+ mHeight = 0;
1123
+ mRequestRender = true;
1124
+ mRenderMode = RENDERMODE_CONTINUOUSLY;
1125
+ mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
1126
+ }
1127
+
1128
+ @Override
1129
+ public void run() {
1130
+ setName("GLThread " + getId());
1131
+ if (LOG_THREADS) {
1132
+ Log.i("GLThread", "starting tid=" + getId());
1133
+ }
1134
+
1135
+ try {
1136
+ guardedRun();
1137
+ } catch (InterruptedException e) {
1138
+ // fall thru and exit normally
1139
+ } finally {
1140
+ sGLThreadManager.threadExiting(this);
1141
+ }
1142
+ }
1143
+
1144
+ /*
1145
+ * This private method should only be called inside a
1146
+ * synchronized(sGLThreadManager) block.
1147
+ */
1148
+ private void stopEglSurfaceLocked() {
1149
+ if (mHaveEglSurface) {
1150
+ mHaveEglSurface = false;
1151
+ mEglHelper.destroySurface();
1152
+ }
1153
+ }
1154
+
1155
+ /*
1156
+ * This private method should only be called inside a
1157
+ * synchronized(sGLThreadManager) block.
1158
+ */
1159
+ private void stopEglContextLocked() {
1160
+ if (mHaveEglContext) {
1161
+ mEglHelper.finish();
1162
+ mHaveEglContext = false;
1163
+ sGLThreadManager.releaseEglContextLocked(this);
1164
+ }
1165
+ }
1166
+ private void guardedRun() throws InterruptedException {
1167
+ mEglHelper = new EglHelper(mGLSurfaceViewWeakRef);
1168
+ mHaveEglContext = false;
1169
+ mHaveEglSurface = false;
1170
+ try {
1171
+ GL10 gl = null;
1172
+ boolean createEglContext = false;
1173
+ boolean createEglSurface = false;
1174
+ boolean createGlInterface = false;
1175
+ boolean lostEglContext = false;
1176
+ boolean sizeChanged = false;
1177
+ boolean wantRenderNotification = false;
1178
+ boolean doRenderNotification = false;
1179
+ boolean askedToReleaseEglContext = false;
1180
+ int w = 0;
1181
+ int h = 0;
1182
+ Runnable event = null;
1183
+
1184
+ while (true) {
1185
+ synchronized (sGLThreadManager) {
1186
+ while (true) {
1187
+ if (mShouldExit) {
1188
+ return;
1189
+ }
1190
+
1191
+ if (! mEventQueue.isEmpty()) {
1192
+ event = mEventQueue.remove(0);
1193
+ break;
1194
+ }
1195
+
1196
+ // Update the pause state.
1197
+ boolean pausing = false;
1198
+ if (mPaused != mRequestPaused) {
1199
+ pausing = mRequestPaused;
1200
+ mPaused = mRequestPaused;
1201
+ sGLThreadManager.notifyAll();
1202
+ if (LOG_PAUSE_RESUME) {
1203
+ Log.i("GLThread", "mPaused is now " + mPaused + " tid=" + getId());
1204
+ }
1205
+ }
1206
+
1207
+ // Do we need to give up the EGL context?
1208
+ if (mShouldReleaseEglContext) {
1209
+ if (LOG_SURFACE) {
1210
+ Log.i("GLThread", "releasing EGL context because asked to tid=" + getId());
1211
+ }
1212
+ stopEglSurfaceLocked();
1213
+ stopEglContextLocked();
1214
+ mShouldReleaseEglContext = false;
1215
+ askedToReleaseEglContext = true;
1216
+ }
1217
+
1218
+ // Have we lost the EGL context?
1219
+ if (lostEglContext) {
1220
+ stopEglSurfaceLocked();
1221
+ stopEglContextLocked();
1222
+ lostEglContext = false;
1223
+ }
1224
+
1225
+ // When pausing, release the EGL surface:
1226
+ if (pausing && mHaveEglSurface) {
1227
+ if (LOG_SURFACE) {
1228
+ Log.i("GLThread", "releasing EGL surface because paused tid=" + getId());
1229
+ }
1230
+ stopEglSurfaceLocked();
1231
+ }
1232
+
1233
+ // When pausing, optionally release the EGL Context:
1234
+ if (pausing && mHaveEglContext) {
1235
+ GLTextureView view = mGLSurfaceViewWeakRef.get();
1236
+ boolean preserveEglContextOnPause = view == null ?
1237
+ false : view.mPreserveEGLContextOnPause;
1238
+ if (!preserveEglContextOnPause || sGLThreadManager.shouldReleaseEGLContextWhenPausing()) {
1239
+ stopEglContextLocked();
1240
+ if (LOG_SURFACE) {
1241
+ Log.i("GLThread", "releasing EGL context because paused tid=" + getId());
1242
+ }
1243
+ }
1244
+ }
1245
+
1246
+ // When pausing, optionally terminate EGL:
1247
+ if (pausing) {
1248
+ if (sGLThreadManager.shouldTerminateEGLWhenPausing()) {
1249
+ mEglHelper.finish();
1250
+ if (LOG_SURFACE) {
1251
+ Log.i("GLThread", "terminating EGL because paused tid=" + getId());
1252
+ }
1253
+ }
1254
+ }
1255
+
1256
+ // Have we lost the SurfaceView surface?
1257
+ if ((! mHasSurface) && (! mWaitingForSurface)) {
1258
+ if (LOG_SURFACE) {
1259
+ Log.i("GLThread", "noticed surfaceView surface lost tid=" + getId());
1260
+ }
1261
+ if (mHaveEglSurface) {
1262
+ stopEglSurfaceLocked();
1263
+ }
1264
+ mWaitingForSurface = true;
1265
+ mSurfaceIsBad = false;
1266
+ sGLThreadManager.notifyAll();
1267
+ }
1268
+
1269
+ // Have we acquired the surface view surface?
1270
+ if (mHasSurface && mWaitingForSurface) {
1271
+ if (LOG_SURFACE) {
1272
+ Log.i("GLThread", "noticed surfaceView surface acquired tid=" + getId());
1273
+ }
1274
+ mWaitingForSurface = false;
1275
+ sGLThreadManager.notifyAll();
1276
+ }
1277
+
1278
+ if (doRenderNotification) {
1279
+ if (LOG_SURFACE) {
1280
+ Log.i("GLThread", "sending render notification tid=" + getId());
1281
+ }
1282
+ wantRenderNotification = false;
1283
+ doRenderNotification = false;
1284
+ mRenderComplete = true;
1285
+ sGLThreadManager.notifyAll();
1286
+ }
1287
+
1288
+ // Ready to draw?
1289
+ if (readyToDraw()) {
1290
+
1291
+ // If we don't have an EGL context, try to acquire one.
1292
+ if (! mHaveEglContext) {
1293
+ if (askedToReleaseEglContext) {
1294
+ askedToReleaseEglContext = false;
1295
+ } else if (sGLThreadManager.tryAcquireEglContextLocked(this)) {
1296
+ try {
1297
+ mEglHelper.start();
1298
+ } catch (RuntimeException t) {
1299
+ sGLThreadManager.releaseEglContextLocked(this);
1300
+ throw t;
1301
+ }
1302
+ mHaveEglContext = true;
1303
+ createEglContext = true;
1304
+
1305
+ sGLThreadManager.notifyAll();
1306
+ }
1307
+ }
1308
+
1309
+ if (mHaveEglContext && !mHaveEglSurface) {
1310
+ mHaveEglSurface = true;
1311
+ createEglSurface = true;
1312
+ createGlInterface = true;
1313
+ sizeChanged = true;
1314
+ }
1315
+
1316
+ if (mHaveEglSurface) {
1317
+ if (mSizeChanged) {
1318
+ sizeChanged = true;
1319
+ w = mWidth;
1320
+ h = mHeight;
1321
+ wantRenderNotification = true;
1322
+ if (LOG_SURFACE) {
1323
+ Log.i("GLThread",
1324
+ "noticing that we want render notification tid="
1325
+ + getId());
1326
+ }
1327
+
1328
+ // Destroy and recreate the EGL surface.
1329
+ createEglSurface = true;
1330
+
1331
+ mSizeChanged = false;
1332
+ }
1333
+ mRequestRender = false;
1334
+ sGLThreadManager.notifyAll();
1335
+ break;
1336
+ }
1337
+ }
1338
+
1339
+ // By design, this is the only place in a GLThread thread where we wait().
1340
+ if (LOG_THREADS) {
1341
+ Log.i("GLThread", "waiting tid=" + getId()
1342
+ + " mHaveEglContext: " + mHaveEglContext
1343
+ + " mHaveEglSurface: " + mHaveEglSurface
1344
+ + " mPaused: " + mPaused
1345
+ + " mHasSurface: " + mHasSurface
1346
+ + " mSurfaceIsBad: " + mSurfaceIsBad
1347
+ + " mWaitingForSurface: " + mWaitingForSurface
1348
+ + " mWidth: " + mWidth
1349
+ + " mHeight: " + mHeight
1350
+ + " mRequestRender: " + mRequestRender
1351
+ + " mRenderMode: " + mRenderMode);
1352
+ }
1353
+ sGLThreadManager.wait();
1354
+ }
1355
+ } // end of synchronized(sGLThreadManager)
1356
+
1357
+ if (event != null) {
1358
+ event.run();
1359
+ event = null;
1360
+ continue;
1361
+ }
1362
+
1363
+ if (createEglSurface) {
1364
+ if (LOG_SURFACE) {
1365
+ Log.w("GLThread", "egl createSurface");
1366
+ }
1367
+ if (!mEglHelper.createSurface()) {
1368
+ synchronized(sGLThreadManager) {
1369
+ mSurfaceIsBad = true;
1370
+ sGLThreadManager.notifyAll();
1371
+ }
1372
+ continue;
1373
+ }
1374
+ createEglSurface = false;
1375
+ }
1376
+
1377
+ if (createGlInterface) {
1378
+ gl = (GL10) mEglHelper.createGL();
1379
+
1380
+ sGLThreadManager.checkGLDriver(gl);
1381
+ createGlInterface = false;
1382
+ }
1383
+
1384
+ if (createEglContext) {
1385
+ if (LOG_RENDERER) {
1386
+ Log.w("GLThread", "onSurfaceCreated");
1387
+ }
1388
+ GLTextureView view = mGLSurfaceViewWeakRef.get();
1389
+ if (view != null) {
1390
+ view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
1391
+ }
1392
+ createEglContext = false;
1393
+ }
1394
+
1395
+ if (sizeChanged) {
1396
+ if (LOG_RENDERER) {
1397
+ Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");
1398
+ }
1399
+ GLTextureView view = mGLSurfaceViewWeakRef.get();
1400
+ if (view != null) {
1401
+ view.mRenderer.onSurfaceChanged(gl, w, h);
1402
+ }
1403
+ sizeChanged = false;
1404
+ }
1405
+
1406
+ if (LOG_RENDERER_DRAW_FRAME) {
1407
+ Log.w("GLThread", "onDrawFrame tid=" + getId());
1408
+ }
1409
+ {
1410
+ GLTextureView view = mGLSurfaceViewWeakRef.get();
1411
+ if (view != null) {
1412
+ view.mRenderer.onDrawFrame(gl);
1413
+ }
1414
+ }
1415
+ int swapError = mEglHelper.swap();
1416
+ switch (swapError) {
1417
+ case EGL10.EGL_SUCCESS:
1418
+ break;
1419
+ case EGL11.EGL_CONTEXT_LOST:
1420
+ if (LOG_SURFACE) {
1421
+ Log.i("GLThread", "egl context lost tid=" + getId());
1422
+ }
1423
+ lostEglContext = true;
1424
+ break;
1425
+ default:
1426
+ // Other errors typically mean that the current surface is bad,
1427
+ // probably because the SurfaceView surface has been destroyed,
1428
+ // but we haven't been notified yet.
1429
+ // Log the error to help developers understand why rendering stopped.
1430
+ EglHelper.logEglErrorAsWarning("GLThread", "eglSwapBuffers", swapError);
1431
+
1432
+ synchronized(sGLThreadManager) {
1433
+ mSurfaceIsBad = true;
1434
+ sGLThreadManager.notifyAll();
1435
+ }
1436
+ break;
1437
+ }
1438
+
1439
+ if (wantRenderNotification) {
1440
+ doRenderNotification = true;
1441
+ }
1442
+ }
1443
+
1444
+ } finally {
1445
+ /*
1446
+ * clean-up everything...
1447
+ */
1448
+ synchronized (sGLThreadManager) {
1449
+ stopEglSurfaceLocked();
1450
+ stopEglContextLocked();
1451
+ }
1452
+ }
1453
+ }
1454
+
1455
+ public boolean ableToDraw() {
1456
+ return mHaveEglContext && mHaveEglSurface && readyToDraw();
1457
+ }
1458
+
1459
+ private boolean readyToDraw() {
1460
+ return (!mPaused) && mHasSurface && (!mSurfaceIsBad)
1461
+ && (mWidth > 0) && (mHeight > 0)
1462
+ && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY));
1463
+ }
1464
+
1465
+ public void setRenderMode(int renderMode) {
1466
+ if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) {
1467
+ throw new IllegalArgumentException("renderMode");
1468
+ }
1469
+ synchronized(sGLThreadManager) {
1470
+ mRenderMode = renderMode;
1471
+ sGLThreadManager.notifyAll();
1472
+ }
1473
+ }
1474
+
1475
+ public int getRenderMode() {
1476
+ synchronized(sGLThreadManager) {
1477
+ return mRenderMode;
1478
+ }
1479
+ }
1480
+
1481
+ public void requestRender() {
1482
+ synchronized(sGLThreadManager) {
1483
+ mRequestRender = true;
1484
+ sGLThreadManager.notifyAll();
1485
+ }
1486
+ }
1487
+
1488
+ public void surfaceCreated() {
1489
+ synchronized(sGLThreadManager) {
1490
+ if (LOG_THREADS) {
1491
+ Log.i("GLThread", "surfaceCreated tid=" + getId());
1492
+ }
1493
+ mHasSurface = true;
1494
+ sGLThreadManager.notifyAll();
1495
+ while((mWaitingForSurface) && (!mExited)) {
1496
+ try {
1497
+ sGLThreadManager.wait();
1498
+ } catch (InterruptedException e) {
1499
+ Thread.currentThread().interrupt();
1500
+ }
1501
+ }
1502
+ }
1503
+ }
1504
+
1505
+ public void surfaceDestroyed() {
1506
+ synchronized(sGLThreadManager) {
1507
+ if (LOG_THREADS) {
1508
+ Log.i("GLThread", "surfaceDestroyed tid=" + getId());
1509
+ }
1510
+ mHasSurface = false;
1511
+ sGLThreadManager.notifyAll();
1512
+ while((!mWaitingForSurface) && (!mExited)) {
1513
+ try {
1514
+ sGLThreadManager.wait();
1515
+ } catch (InterruptedException e) {
1516
+ Thread.currentThread().interrupt();
1517
+ }
1518
+ }
1519
+ }
1520
+ }
1521
+
1522
+ public void onPause() {
1523
+ synchronized (sGLThreadManager) {
1524
+ if (LOG_PAUSE_RESUME) {
1525
+ Log.i("GLThread", "onPause tid=" + getId());
1526
+ }
1527
+ mRequestPaused = true;
1528
+ sGLThreadManager.notifyAll();
1529
+ while ((! mExited) && (! mPaused)) {
1530
+ if (LOG_PAUSE_RESUME) {
1531
+ Log.i("Main thread", "onPause waiting for mPaused.");
1532
+ }
1533
+ try {
1534
+ sGLThreadManager.wait();
1535
+ } catch (InterruptedException ex) {
1536
+ Thread.currentThread().interrupt();
1537
+ }
1538
+ }
1539
+ }
1540
+ }
1541
+
1542
+ public void onResume() {
1543
+ synchronized (sGLThreadManager) {
1544
+ if (LOG_PAUSE_RESUME) {
1545
+ Log.i("GLThread", "onResume tid=" + getId());
1546
+ }
1547
+ mRequestPaused = false;
1548
+ mRequestRender = true;
1549
+ mRenderComplete = false;
1550
+ sGLThreadManager.notifyAll();
1551
+ while ((! mExited) && mPaused && (!mRenderComplete)) {
1552
+ if (LOG_PAUSE_RESUME) {
1553
+ Log.i("Main thread", "onResume waiting for !mPaused.");
1554
+ }
1555
+ try {
1556
+ sGLThreadManager.wait();
1557
+ } catch (InterruptedException ex) {
1558
+ Thread.currentThread().interrupt();
1559
+ }
1560
+ }
1561
+ }
1562
+ }
1563
+
1564
+ public void onWindowResize(int w, int h) {
1565
+ synchronized (sGLThreadManager) {
1566
+ mWidth = w;
1567
+ mHeight = h;
1568
+ mSizeChanged = true;
1569
+ mRequestRender = true;
1570
+ mRenderComplete = false;
1571
+ sGLThreadManager.notifyAll();
1572
+
1573
+ // Wait for thread to react to resize and render a frame
1574
+ while (! mExited && !mPaused && !mRenderComplete
1575
+ && ableToDraw()) {
1576
+ if (LOG_SURFACE) {
1577
+ Log.i("Main thread", "onWindowResize waiting for render complete from tid=" + getId());
1578
+ }
1579
+ try {
1580
+ sGLThreadManager.wait();
1581
+ } catch (InterruptedException ex) {
1582
+ Thread.currentThread().interrupt();
1583
+ }
1584
+ }
1585
+ }
1586
+ }
1587
+
1588
+ public void requestExitAndWait() {
1589
+ // don't call this from GLThread thread or it is a guaranteed
1590
+ // deadlock!
1591
+ synchronized(sGLThreadManager) {
1592
+ mShouldExit = true;
1593
+ sGLThreadManager.notifyAll();
1594
+ while (! mExited) {
1595
+ try {
1596
+ sGLThreadManager.wait();
1597
+ } catch (InterruptedException ex) {
1598
+ Thread.currentThread().interrupt();
1599
+ }
1600
+ }
1601
+ }
1602
+ }
1603
+
1604
+ public void requestReleaseEglContextLocked() {
1605
+ mShouldReleaseEglContext = true;
1606
+ sGLThreadManager.notifyAll();
1607
+ }
1608
+
1609
+ /**
1610
+ * Queue an "event" to be run on the GL rendering thread.
1611
+ * @param r the runnable to be run on the GL rendering thread.
1612
+ */
1613
+ public void queueEvent(Runnable r) {
1614
+ if (r == null) {
1615
+ throw new IllegalArgumentException("r must not be null");
1616
+ }
1617
+ synchronized(sGLThreadManager) {
1618
+ mEventQueue.add(r);
1619
+ sGLThreadManager.notifyAll();
1620
+ }
1621
+ }
1622
+
1623
+ // Once the thread is started, all accesses to the following member
1624
+ // variables are protected by the sGLThreadManager monitor
1625
+ private boolean mShouldExit;
1626
+ private boolean mExited;
1627
+ private boolean mRequestPaused;
1628
+ private boolean mPaused;
1629
+ private boolean mHasSurface;
1630
+ private boolean mSurfaceIsBad;
1631
+ private boolean mWaitingForSurface;
1632
+ private boolean mHaveEglContext;
1633
+ private boolean mHaveEglSurface;
1634
+ private boolean mShouldReleaseEglContext;
1635
+ private int mWidth;
1636
+ private int mHeight;
1637
+ private int mRenderMode;
1638
+ private boolean mRequestRender;
1639
+ private boolean mRenderComplete;
1640
+ private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>();
1641
+ private boolean mSizeChanged = true;
1642
+
1643
+ // End of member variables protected by the sGLThreadManager monitor.
1644
+
1645
+ private EglHelper mEglHelper;
1646
+
1647
+ /**
1648
+ * Set once at thread construction time, nulled out when the parent view is garbage
1649
+ * called. This weak reference allows the GLTextureView to be garbage collected while
1650
+ * the GLThread is still alive.
1651
+ */
1652
+ private WeakReference<GLTextureView> mGLSurfaceViewWeakRef;
1653
+
1654
+ }
1655
+
1656
+ static class LogWriter extends Writer {
1657
+
1658
+ @Override public void close() {
1659
+ flushBuilder();
1660
+ }
1661
+
1662
+ @Override public void flush() {
1663
+ flushBuilder();
1664
+ }
1665
+
1666
+ @Override public void write(char[] buf, int offset, int count) {
1667
+ for(int i = 0; i < count; i++) {
1668
+ char c = buf[offset + i];
1669
+ if ( c == '\n') {
1670
+ flushBuilder();
1671
+ }
1672
+ else {
1673
+ mBuilder.append(c);
1674
+ }
1675
+ }
1676
+ }
1677
+
1678
+ private void flushBuilder() {
1679
+ if (mBuilder.length() > 0) {
1680
+ Log.v("GLTextureView", mBuilder.toString());
1681
+ mBuilder.delete(0, mBuilder.length());
1682
+ }
1683
+ }
1684
+
1685
+ private StringBuilder mBuilder = new StringBuilder();
1686
+ }
1687
+
1688
+
1689
+ private void checkRenderThreadState() {
1690
+ if (mGLThread != null) {
1691
+ throw new IllegalStateException(
1692
+ "setRenderer has already been called for this instance.");
1693
+ }
1694
+ }
1695
+
1696
+ private static class GLThreadManager {
1697
+ private static String TAG = "GLThreadManager";
1698
+
1699
+ public synchronized void threadExiting(GLThread thread) {
1700
+ if (LOG_THREADS) {
1701
+ Log.i("GLThread", "exiting tid=" + thread.getId());
1702
+ }
1703
+ thread.mExited = true;
1704
+ if (mEglOwner == thread) {
1705
+ mEglOwner = null;
1706
+ }
1707
+ notifyAll();
1708
+ }
1709
+
1710
+ /*
1711
+ * Tries once to acquire the right to use an EGL
1712
+ * context. Does not block. Requires that we are already
1713
+ * in the sGLThreadManager monitor when this is called.
1714
+ *
1715
+ * @return true if the right to use an EGL context was acquired.
1716
+ */
1717
+ public boolean tryAcquireEglContextLocked(GLThread thread) {
1718
+ if (mEglOwner == thread || mEglOwner == null) {
1719
+ mEglOwner = thread;
1720
+ notifyAll();
1721
+ return true;
1722
+ }
1723
+ checkGLESVersion();
1724
+ if (mMultipleGLESContextsAllowed) {
1725
+ return true;
1726
+ }
1727
+ // Notify the owning thread that it should release the context.
1728
+ // TODO: implement a fairness policy. Currently
1729
+ // if the owning thread is drawing continuously it will just
1730
+ // reacquire the EGL context.
1731
+ if (mEglOwner != null) {
1732
+ mEglOwner.requestReleaseEglContextLocked();
1733
+ }
1734
+ return false;
1735
+ }
1736
+
1737
+ /*
1738
+ * Releases the EGL context. Requires that we are already in the
1739
+ * sGLThreadManager monitor when this is called.
1740
+ */
1741
+ public void releaseEglContextLocked(GLThread thread) {
1742
+ if (mEglOwner == thread) {
1743
+ mEglOwner = null;
1744
+ }
1745
+ notifyAll();
1746
+ }
1747
+
1748
+ public synchronized boolean shouldReleaseEGLContextWhenPausing() {
1749
+ // Release the EGL context when pausing even if
1750
+ // the hardware supports multiple EGL contexts.
1751
+ // Otherwise the device could run out of EGL contexts.
1752
+ return mLimitedGLESContexts;
1753
+ }
1754
+
1755
+ public synchronized boolean shouldTerminateEGLWhenPausing() {
1756
+ checkGLESVersion();
1757
+ return !mMultipleGLESContextsAllowed;
1758
+ }
1759
+
1760
+ public synchronized void checkGLDriver(GL10 gl) {
1761
+ if (! mGLESDriverCheckComplete) {
1762
+ checkGLESVersion();
1763
+ String renderer = gl.glGetString(GL10.GL_RENDERER);
1764
+ if (mGLESVersion < kGLES_20) {
1765
+ mMultipleGLESContextsAllowed =
1766
+ ! renderer.startsWith(kMSM7K_RENDERER_PREFIX);
1767
+ notifyAll();
1768
+ }
1769
+ mLimitedGLESContexts = !mMultipleGLESContextsAllowed;
1770
+ if (LOG_SURFACE) {
1771
+ Log.w(TAG, "checkGLDriver renderer = \"" + renderer + "\" multipleContextsAllowed = "
1772
+ + mMultipleGLESContextsAllowed
1773
+ + " mLimitedGLESContexts = " + mLimitedGLESContexts);
1774
+ }
1775
+ mGLESDriverCheckComplete = true;
1776
+ }
1777
+ }
1778
+
1779
+ private void checkGLESVersion() {
1780
+ if (! mGLESVersionCheckComplete) {
1781
+ // mGLESVersion = SystemProperties.getInt(
1782
+ // "ro.opengles.version",
1783
+ // ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
1784
+ // if (mGLESVersion >= kGLES_20) {
1785
+ // mMultipleGLESContextsAllowed = true;
1786
+ // }
1787
+ // if (LOG_SURFACE) {
1788
+ // Log.w(TAG, "checkGLESVersion mGLESVersion =" +
1789
+ // " " + mGLESVersion + " mMultipleGLESContextsAllowed = " + mMultipleGLESContextsAllowed);
1790
+ // }
1791
+ mGLESVersionCheckComplete = true;
1792
+ }
1793
+ }
1794
+
1795
+ /**
1796
+ * This check was required for some pre-Android-3.0 hardware. Android 3.0 provides
1797
+ * support for hardware-accelerated views, therefore multiple EGL contexts are
1798
+ * supported on all Android 3.0+ EGL drivers.
1799
+ */
1800
+ private boolean mGLESVersionCheckComplete;
1801
+ private int mGLESVersion;
1802
+ private boolean mGLESDriverCheckComplete;
1803
+ private boolean mMultipleGLESContextsAllowed;
1804
+ private boolean mLimitedGLESContexts;
1805
+ private static final int kGLES_20 = 0x20000;
1806
+ private static final String kMSM7K_RENDERER_PREFIX =
1807
+ "Q3Dimension MSM7500 ";
1808
+ private GLThread mEglOwner;
1809
+ }
1810
+
1811
+ private static final GLThreadManager sGLThreadManager = new GLThreadManager();
1812
+
1813
+ private final WeakReference<GLTextureView> mThisWeakRef =
1814
+ new WeakReference<GLTextureView>(this);
1815
+ private GLThread mGLThread;
1816
+ private Renderer mRenderer;
1817
+ private boolean mDetached;
1818
+ private EGLConfigChooser mEGLConfigChooser;
1819
+ private EGLContextFactory mEGLContextFactory;
1820
+ private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;
1821
+ private GLWrapper mGLWrapper;
1822
+ private int mDebugFlags;
1823
+ private int mEGLContextClientVersion;
1824
+ private boolean mPreserveEGLContextOnPause;
1825
+ }