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