Home | History | Annotate | Download | only in opengl
      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.util.AttributeSet;
     36 import android.util.Log;
     37 import android.view.SurfaceHolder;
     38 import android.view.SurfaceView;
     39 
     40 /**
     41  * An implementation of SurfaceView that uses the dedicated surface for
     42  * displaying OpenGL rendering.
     43  * <p>
     44  * A GLSurfaceView provides the following features:
     45  * <p>
     46  * <ul>
     47  * <li>Manages a surface, which is a special piece of memory that can be
     48  * composited into the Android view system.
     49  * <li>Manages an EGL display, which enables OpenGL to render into a surface.
     50  * <li>Accepts a user-provided Renderer object that does the actual rendering.
     51  * <li>Renders on a dedicated thread to decouple rendering performance from the
     52  * UI thread.
     53  * <li>Supports both on-demand and continuous rendering.
     54  * <li>Optionally wraps, traces, and/or error-checks the renderer's OpenGL calls.
     55  * </ul>
     56  *
     57  * <div class="special reference">
     58  * <h3>Developer Guides</h3>
     59  * <p>For more information about how to use OpenGL, read the
     60  * <a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a> developer guide.</p>
     61  * </div>
     62  *
     63  * <h3>Using GLSurfaceView</h3>
     64  * <p>
     65  * Typically you use GLSurfaceView by subclassing it and overriding one or more of the
     66  * View system input event methods. If your application does not need to override event
     67  * methods then GLSurfaceView can be used as-is. For the most part
     68  * GLSurfaceView behavior is customized by calling "set" methods rather than by subclassing.
     69  * For example, unlike a regular View, drawing is delegated to a separate Renderer object which
     70  * is registered with the GLSurfaceView
     71  * using the {@link #setRenderer(Renderer)} call.
     72  * <p>
     73  * <h3>Initializing GLSurfaceView</h3>
     74  * All you have to do to initialize a GLSurfaceView is call {@link #setRenderer(Renderer)}.
     75  * However, if desired, you can modify the default behavior of GLSurfaceView by calling one or
     76  * more of these methods before calling setRenderer:
     77  * <ul>
     78  * <li>{@link #setDebugFlags(int)}
     79  * <li>{@link #setEGLConfigChooser(boolean)}
     80  * <li>{@link #setEGLConfigChooser(EGLConfigChooser)}
     81  * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)}
     82  * <li>{@link #setGLWrapper(GLWrapper)}
     83  * </ul>
     84  * <p>
     85  * <h4>Specifying the android.view.Surface</h4>
     86  * By default GLSurfaceView will create a PixelFormat.RGB_888 format surface. If a translucent
     87  * surface is required, call getHolder().setFormat(PixelFormat.TRANSLUCENT).
     88  * The exact format of a TRANSLUCENT surface is device dependent, but it will be
     89  * a 32-bit-per-pixel surface with 8 bits per component.
     90  * <p>
     91  * <h4>Choosing an EGL Configuration</h4>
     92  * A given Android device may support multiple EGLConfig rendering configurations.
     93  * The available configurations may differ in how may channels of data are present, as
     94  * well as how many bits are allocated to each channel. Therefore, the first thing
     95  * GLSurfaceView has to do when starting to render is choose what EGLConfig to use.
     96  * <p>
     97  * By default GLSurfaceView chooses a EGLConfig that has an RGB_888 pixel format,
     98  * with at least a 16-bit depth buffer and no stencil.
     99  * <p>
    100  * If you would prefer a different EGLConfig
    101  * you can override the default behavior by calling one of the
    102  * setEGLConfigChooser methods.
    103  * <p>
    104  * <h4>Debug Behavior</h4>
    105  * You can optionally modify the behavior of GLSurfaceView by calling
    106  * one or more of the debugging methods {@link #setDebugFlags(int)},
    107  * and {@link #setGLWrapper}. These methods may be called before and/or after setRenderer, but
    108  * typically they are called before setRenderer so that they take effect immediately.
    109  * <p>
    110  * <h4>Setting a Renderer</h4>
    111  * Finally, you must call {@link #setRenderer} to register a {@link Renderer}.
    112  * The renderer is
    113  * responsible for doing the actual OpenGL rendering.
    114  * <p>
    115  * <h3>Rendering Mode</h3>
    116  * Once the renderer is set, you can control whether the renderer draws
    117  * continuously or on-demand by calling
    118  * {@link #setRenderMode}. The default is continuous rendering.
    119  * <p>
    120  * <h3>Activity Life-cycle</h3>
    121  * A GLSurfaceView must be notified when the activity is paused and resumed. GLSurfaceView clients
    122  * are required to call {@link #onPause()} when the activity pauses and
    123  * {@link #onResume()} when the activity resumes. These calls allow GLSurfaceView to
    124  * pause and resume the rendering thread, and also allow GLSurfaceView to release and recreate
    125  * the OpenGL display.
    126  * <p>
    127  * <h3>Handling events</h3>
    128  * <p>
    129  * To handle an event you will typically subclass GLSurfaceView and override the
    130  * appropriate method, just as you would with any other View. However, when handling
    131  * the event, you may need to communicate with the Renderer object
    132  * that's running in the rendering thread. You can do this using any
    133  * standard Java cross-thread communication mechanism. In addition,
    134  * one relatively easy way to communicate with your renderer is
    135  * to call
    136  * {@link #queueEvent(Runnable)}. For example:
    137  * <pre class="prettyprint">
    138  * class MyGLSurfaceView extends GLSurfaceView {
    139  *
    140  *     private MyRenderer mMyRenderer;
    141  *
    142  *     public void start() {
    143  *         mMyRenderer = ...;
    144  *         setRenderer(mMyRenderer);
    145  *     }
    146  *
    147  *     public boolean onKeyDown(int keyCode, KeyEvent event) {
    148  *         if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
    149  *             queueEvent(new Runnable() {
    150  *                 // This method will be called on the rendering
    151  *                 // thread:
    152  *                 public void run() {
    153  *                     mMyRenderer.handleDpadCenter();
    154  *                 }});
    155  *             return true;
    156  *         }
    157  *         return super.onKeyDown(keyCode, event);
    158  *     }
    159  * }
    160  * </pre>
    161  *
    162  */
    163 public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
    164     private final static String TAG = "GLSurfaceView";
    165     private final static boolean LOG_ATTACH_DETACH = false;
    166     private final static boolean LOG_THREADS = false;
    167     private final static boolean LOG_PAUSE_RESUME = false;
    168     private final static boolean LOG_SURFACE = false;
    169     private final static boolean LOG_RENDERER = false;
    170     private final static boolean LOG_RENDERER_DRAW_FRAME = false;
    171     private final static boolean LOG_EGL = false;
    172     /**
    173      * The renderer only renders
    174      * when the surface is created, or when {@link #requestRender} is called.
    175      *
    176      * @see #getRenderMode()
    177      * @see #setRenderMode(int)
    178      * @see #requestRender()
    179      */
    180     public final static int RENDERMODE_WHEN_DIRTY = 0;
    181     /**
    182      * The renderer is called
    183      * continuously to re-render the scene.
    184      *
    185      * @see #getRenderMode()
    186      * @see #setRenderMode(int)
    187      */
    188     public final static int RENDERMODE_CONTINUOUSLY = 1;
    189 
    190     /**
    191      * Check glError() after every GL call and throw an exception if glError indicates
    192      * that an error has occurred. This can be used to help track down which OpenGL ES call
    193      * is causing an error.
    194      *
    195      * @see #getDebugFlags
    196      * @see #setDebugFlags
    197      */
    198     public final static int DEBUG_CHECK_GL_ERROR = 1;
    199 
    200     /**
    201      * Log GL calls to the system log at "verbose" level with tag "GLSurfaceView".
    202      *
    203      * @see #getDebugFlags
    204      * @see #setDebugFlags
    205      */
    206     public final static int DEBUG_LOG_GL_CALLS = 2;
    207 
    208     /**
    209      * Standard View constructor. In order to render something, you
    210      * must call {@link #setRenderer} to register a renderer.
    211      */
    212     public GLSurfaceView(Context context) {
    213         super(context);
    214         init();
    215     }
    216 
    217     /**
    218      * Standard View constructor. In order to render something, you
    219      * must call {@link #setRenderer} to register a renderer.
    220      */
    221     public GLSurfaceView(Context context, AttributeSet attrs) {
    222         super(context, attrs);
    223         init();
    224     }
    225 
    226     @Override
    227     protected void finalize() throws Throwable {
    228         try {
    229             if (mGLThread != null) {
    230                 // GLThread may still be running if this view was never
    231                 // attached to a window.
    232                 mGLThread.requestExitAndWait();
    233             }
    234         } finally {
    235             super.finalize();
    236         }
    237     }
    238 
    239     private void init() {
    240         // Install a SurfaceHolder.Callback so we get notified when the
    241         // underlying surface is created and destroyed
    242         SurfaceHolder holder = getHolder();
    243         holder.addCallback(this);
    244         // setFormat is done by SurfaceView in SDK 2.3 and newer. Uncomment
    245         // this statement if back-porting to 2.2 or older:
    246         // holder.setFormat(PixelFormat.RGB_565);
    247         //
    248         // setType is not needed for SDK 2.0 or newer. Uncomment this
    249         // statement if back-porting this code to older SDKs.
    250         // holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
    251     }
    252 
    253     /**
    254      * Set the glWrapper. If the glWrapper is not null, its
    255      * {@link GLWrapper#wrap(GL)} method is called
    256      * whenever a surface is created. A GLWrapper can be used to wrap
    257      * the GL object that's passed to the renderer. Wrapping a GL
    258      * object enables examining and modifying the behavior of the
    259      * GL calls made by the renderer.
    260      * <p>
    261      * Wrapping is typically used for debugging purposes.
    262      * <p>
    263      * The default value is null.
    264      * @param glWrapper the new GLWrapper
    265      */
    266     public void setGLWrapper(GLWrapper glWrapper) {
    267         mGLWrapper = glWrapper;
    268     }
    269 
    270     /**
    271      * Set the debug flags to a new value. The value is
    272      * constructed by OR-together zero or more
    273      * of the DEBUG_CHECK_* constants. The debug flags take effect
    274      * whenever a surface is created. The default value is zero.
    275      * @param debugFlags the new debug flags
    276      * @see #DEBUG_CHECK_GL_ERROR
    277      * @see #DEBUG_LOG_GL_CALLS
    278      */
    279     public void setDebugFlags(int debugFlags) {
    280         mDebugFlags = debugFlags;
    281     }
    282 
    283     /**
    284      * Get the current value of the debug flags.
    285      * @return the current value of the debug flags.
    286      */
    287     public int getDebugFlags() {
    288         return mDebugFlags;
    289     }
    290 
    291     /**
    292      * Control whether the EGL context is preserved when the GLSurfaceView is paused and
    293      * resumed.
    294      * <p>
    295      * If set to true, then the EGL context may be preserved when the GLSurfaceView is paused.
    296      * Whether the EGL context is actually preserved or not depends upon whether the
    297      * Android device that the program is running on can support an arbitrary number of EGL
    298      * contexts or not. Devices that can only support a limited number of EGL contexts must
    299      * release the  EGL context in order to allow multiple applications to share the GPU.
    300      * <p>
    301      * If set to false, the EGL context will be released when the GLSurfaceView is paused,
    302      * and recreated when the GLSurfaceView is resumed.
    303      * <p>
    304      *
    305      * The default is false.
    306      *
    307      * @param preserveOnPause preserve the EGL context when paused
    308      */
    309     public void setPreserveEGLContextOnPause(boolean preserveOnPause) {
    310         mPreserveEGLContextOnPause = preserveOnPause;
    311     }
    312 
    313     /**
    314      * @return true if the EGL context will be preserved when paused
    315      */
    316     public boolean getPreserveEGLContextOnPause() {
    317         return mPreserveEGLContextOnPause;
    318     }
    319 
    320     /**
    321      * Set the renderer associated with this view. Also starts the thread that
    322      * will call the renderer, which in turn causes the rendering to start.
    323      * <p>This method should be called once and only once in the life-cycle of
    324      * a GLSurfaceView.
    325      * <p>The following GLSurfaceView methods can only be called <em>before</em>
    326      * setRenderer is called:
    327      * <ul>
    328      * <li>{@link #setEGLConfigChooser(boolean)}
    329      * <li>{@link #setEGLConfigChooser(EGLConfigChooser)}
    330      * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)}
    331      * </ul>
    332      * <p>
    333      * The following GLSurfaceView methods can only be called <em>after</em>
    334      * setRenderer is called:
    335      * <ul>
    336      * <li>{@link #getRenderMode()}
    337      * <li>{@link #onPause()}
    338      * <li>{@link #onResume()}
    339      * <li>{@link #queueEvent(Runnable)}
    340      * <li>{@link #requestRender()}
    341      * <li>{@link #setRenderMode(int)}
    342      * </ul>
    343      *
    344      * @param renderer the renderer to use to perform OpenGL drawing.
    345      */
    346     public void setRenderer(Renderer renderer) {
    347         checkRenderThreadState();
    348         if (mEGLConfigChooser == null) {
    349             mEGLConfigChooser = new SimpleEGLConfigChooser(true);
    350         }
    351         if (mEGLContextFactory == null) {
    352             mEGLContextFactory = new DefaultContextFactory();
    353         }
    354         if (mEGLWindowSurfaceFactory == null) {
    355             mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
    356         }
    357         mRenderer = renderer;
    358         mGLThread = new GLThread(mThisWeakRef);
    359         mGLThread.start();
    360     }
    361 
    362     /**
    363      * Install a custom EGLContextFactory.
    364      * <p>If this method is
    365      * called, it must be called before {@link #setRenderer(Renderer)}
    366      * is called.
    367      * <p>
    368      * If this method is not called, then by default
    369      * a context will be created with no shared context and
    370      * with a null attribute list.
    371      */
    372     public void setEGLContextFactory(EGLContextFactory factory) {
    373         checkRenderThreadState();
    374         mEGLContextFactory = factory;
    375     }
    376 
    377     /**
    378      * Install a custom EGLWindowSurfaceFactory.
    379      * <p>If this method is
    380      * called, it must be called before {@link #setRenderer(Renderer)}
    381      * is called.
    382      * <p>
    383      * If this method is not called, then by default
    384      * a window surface will be created with a null attribute list.
    385      */
    386     public void setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory factory) {
    387         checkRenderThreadState();
    388         mEGLWindowSurfaceFactory = factory;
    389     }
    390 
    391     /**
    392      * Install a custom EGLConfigChooser.
    393      * <p>If this method is
    394      * called, it must be called before {@link #setRenderer(Renderer)}
    395      * is called.
    396      * <p>
    397      * If no setEGLConfigChooser method is called, then by default the
    398      * view will choose an EGLConfig that is compatible with the current
    399      * android.view.Surface, with a depth buffer depth of
    400      * at least 16 bits.
    401      * @param configChooser
    402      */
    403     public void setEGLConfigChooser(EGLConfigChooser configChooser) {
    404         checkRenderThreadState();
    405         mEGLConfigChooser = configChooser;
    406     }
    407 
    408     /**
    409      * Install a config chooser which will choose a config
    410      * as close to 16-bit RGB as possible, with or without an optional depth
    411      * buffer as close to 16-bits as possible.
    412      * <p>If this method is
    413      * called, it must be called before {@link #setRenderer(Renderer)}
    414      * is called.
    415      * <p>
    416      * If no setEGLConfigChooser method is called, then by default the
    417      * view will choose an RGB_888 surface with a depth buffer depth of
    418      * at least 16 bits.
    419      *
    420      * @param needDepth
    421      */
    422     public void setEGLConfigChooser(boolean needDepth) {
    423         setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth));
    424     }
    425 
    426     /**
    427      * Install a config chooser which will choose a config
    428      * with at least the specified depthSize and stencilSize,
    429      * and exactly the specified redSize, greenSize, blueSize and alphaSize.
    430      * <p>If this method is
    431      * called, it must be called before {@link #setRenderer(Renderer)}
    432      * is called.
    433      * <p>
    434      * If no setEGLConfigChooser method is called, then by default the
    435      * view will choose an RGB_888 surface with a depth buffer depth of
    436      * at least 16 bits.
    437      *
    438      */
    439     public void setEGLConfigChooser(int redSize, int greenSize, int blueSize,
    440             int alphaSize, int depthSize, int stencilSize) {
    441         setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize,
    442                 blueSize, alphaSize, depthSize, stencilSize));
    443     }
    444 
    445     /**
    446      * Inform the default EGLContextFactory and default EGLConfigChooser
    447      * which EGLContext client version to pick.
    448      * <p>Use this method to create an OpenGL ES 2.0-compatible context.
    449      * Example:
    450      * <pre class="prettyprint">
    451      *     public MyView(Context context) {
    452      *         super(context);
    453      *         setEGLContextClientVersion(2); // Pick an OpenGL ES 2.0 context.
    454      *         setRenderer(new MyRenderer());
    455      *     }
    456      * </pre>
    457      * <p>Note: Activities which require OpenGL ES 2.0 should indicate this by
    458      * setting @lt;uses-feature android:glEsVersion="0x00020000" /> in the activity's
    459      * AndroidManifest.xml file.
    460      * <p>If this method is called, it must be called before {@link #setRenderer(Renderer)}
    461      * is called.
    462      * <p>This method only affects the behavior of the default EGLContexFactory and the
    463      * default EGLConfigChooser. If
    464      * {@link #setEGLContextFactory(EGLContextFactory)} has been called, then the supplied
    465      * EGLContextFactory is responsible for creating an OpenGL ES 2.0-compatible context.
    466      * If
    467      * {@link #setEGLConfigChooser(EGLConfigChooser)} has been called, then the supplied
    468      * EGLConfigChooser is responsible for choosing an OpenGL ES 2.0-compatible config.
    469      * @param version The EGLContext client version to choose. Use 2 for OpenGL ES 2.0
    470      */
    471     public void setEGLContextClientVersion(int version) {
    472         checkRenderThreadState();
    473         mEGLContextClientVersion = version;
    474     }
    475 
    476     /**
    477      * Set the rendering mode. When renderMode is
    478      * RENDERMODE_CONTINUOUSLY, the renderer is called
    479      * repeatedly to re-render the scene. When renderMode
    480      * is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface
    481      * is created, or when {@link #requestRender} is called. Defaults to RENDERMODE_CONTINUOUSLY.
    482      * <p>
    483      * Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system performance
    484      * by allowing the GPU and CPU to idle when the view does not need to be updated.
    485      * <p>
    486      * This method can only be called after {@link #setRenderer(Renderer)}
    487      *
    488      * @param renderMode one of the RENDERMODE_X constants
    489      * @see #RENDERMODE_CONTINUOUSLY
    490      * @see #RENDERMODE_WHEN_DIRTY
    491      */
    492     public void setRenderMode(int renderMode) {
    493         mGLThread.setRenderMode(renderMode);
    494     }
    495 
    496     /**
    497      * Get the current rendering mode. May be called
    498      * from any thread. Must not be called before a renderer has been set.
    499      * @return the current rendering mode.
    500      * @see #RENDERMODE_CONTINUOUSLY
    501      * @see #RENDERMODE_WHEN_DIRTY
    502      */
    503     public int getRenderMode() {
    504         return mGLThread.getRenderMode();
    505     }
    506 
    507     /**
    508      * Request that the renderer render a frame.
    509      * This method is typically used when the render mode has been set to
    510      * {@link #RENDERMODE_WHEN_DIRTY}, so that frames are only rendered on demand.
    511      * May be called
    512      * from any thread. Must not be called before a renderer has been set.
    513      */
    514     public void requestRender() {
    515         mGLThread.requestRender();
    516     }
    517 
    518     /**
    519      * This method is part of the SurfaceHolder.Callback interface, and is
    520      * not normally called or subclassed by clients of GLSurfaceView.
    521      */
    522     public void surfaceCreated(SurfaceHolder holder) {
    523         mGLThread.surfaceCreated();
    524     }
    525 
    526     /**
    527      * This method is part of the SurfaceHolder.Callback interface, and is
    528      * not normally called or subclassed by clients of GLSurfaceView.
    529      */
    530     public void surfaceDestroyed(SurfaceHolder holder) {
    531         // Surface will be destroyed when we return
    532         mGLThread.surfaceDestroyed();
    533     }
    534 
    535     /**
    536      * This method is part of the SurfaceHolder.Callback interface, and is
    537      * not normally called or subclassed by clients of GLSurfaceView.
    538      */
    539     public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
    540         mGLThread.onWindowResize(w, h);
    541     }
    542 
    543     /**
    544      * Inform the view that the activity is paused. The owner of this view must
    545      * call this method when the activity is paused. Calling this method will
    546      * pause the rendering thread.
    547      * Must not be called before a renderer has been set.
    548      */
    549     public void onPause() {
    550         mGLThread.onPause();
    551     }
    552 
    553     /**
    554      * Inform the view that the activity is resumed. The owner of this view must
    555      * call this method when the activity is resumed. Calling this method will
    556      * recreate the OpenGL display and resume the rendering
    557      * thread.
    558      * Must not be called before a renderer has been set.
    559      */
    560     public void onResume() {
    561         mGLThread.onResume();
    562     }
    563 
    564     /**
    565      * Queue a runnable to be run on the GL rendering thread. This can be used
    566      * to communicate with the Renderer on the rendering thread.
    567      * Must not be called before a renderer has been set.
    568      * @param r the runnable to be run on the GL rendering thread.
    569      */
    570     public void queueEvent(Runnable r) {
    571         mGLThread.queueEvent(r);
    572     }
    573 
    574     /**
    575      * This method is used as part of the View class and is not normally
    576      * called or subclassed by clients of GLSurfaceView.
    577      */
    578     @Override
    579     protected void onAttachedToWindow() {
    580         super.onAttachedToWindow();
    581         if (LOG_ATTACH_DETACH) {
    582             Log.d(TAG, "onAttachedToWindow reattach =" + mDetached);
    583         }
    584         if (mDetached && (mRenderer != null)) {
    585             int renderMode = RENDERMODE_CONTINUOUSLY;
    586             if (mGLThread != null) {
    587                 renderMode = mGLThread.getRenderMode();
    588             }
    589             mGLThread = new GLThread(mThisWeakRef);
    590             if (renderMode != RENDERMODE_CONTINUOUSLY) {
    591                 mGLThread.setRenderMode(renderMode);
    592             }
    593             mGLThread.start();
    594         }
    595         mDetached = false;
    596     }
    597 
    598     /**
    599      * This method is used as part of the View class and is not normally
    600      * called or subclassed by clients of GLSurfaceView.
    601      * Must not be called before a renderer has been set.
    602      */
    603     @Override
    604     protected void onDetachedFromWindow() {
    605         if (LOG_ATTACH_DETACH) {
    606             Log.d(TAG, "onDetachedFromWindow");
    607         }
    608         if (mGLThread != null) {
    609             mGLThread.requestExitAndWait();
    610         }
    611         mDetached = true;
    612         super.onDetachedFromWindow();
    613     }
    614 
    615     // ----------------------------------------------------------------------
    616 
    617     /**
    618      * An interface used to wrap a GL interface.
    619      * <p>Typically
    620      * used for implementing debugging and tracing on top of the default
    621      * GL interface. You would typically use this by creating your own class
    622      * that implemented all the GL methods by delegating to another GL instance.
    623      * Then you could add your own behavior before or after calling the
    624      * delegate. All the GLWrapper would do was instantiate and return the
    625      * wrapper GL instance:
    626      * <pre class="prettyprint">
    627      * class MyGLWrapper implements GLWrapper {
    628      *     GL wrap(GL gl) {
    629      *         return new MyGLImplementation(gl);
    630      *     }
    631      *     static class MyGLImplementation implements GL,GL10,GL11,... {
    632      *         ...
    633      *     }
    634      * }
    635      * </pre>
    636      * @see #setGLWrapper(GLWrapper)
    637      */
    638     public interface GLWrapper {
    639         /**
    640          * Wraps a gl interface in another gl interface.
    641          * @param gl a GL interface that is to be wrapped.
    642          * @return either the input argument or another GL object that wraps the input argument.
    643          */
    644         GL wrap(GL gl);
    645     }
    646 
    647     /**
    648      * A generic renderer interface.
    649      * <p>
    650      * The renderer is responsible for making OpenGL calls to render a frame.
    651      * <p>
    652      * GLSurfaceView clients typically create their own classes that implement
    653      * this interface, and then call {@link GLSurfaceView#setRenderer} to
    654      * register the renderer with the GLSurfaceView.
    655      * <p>
    656      *
    657      * <div class="special reference">
    658      * <h3>Developer Guides</h3>
    659      * <p>For more information about how to use OpenGL, read the
    660      * <a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a> developer guide.</p>
    661      * </div>
    662      *
    663      * <h3>Threading</h3>
    664      * The renderer will be called on a separate thread, so that rendering
    665      * performance is decoupled from the UI thread. Clients typically need to
    666      * communicate with the renderer from the UI thread, because that's where
    667      * input events are received. Clients can communicate using any of the
    668      * standard Java techniques for cross-thread communication, or they can
    669      * use the {@link GLSurfaceView#queueEvent(Runnable)} convenience method.
    670      * <p>
    671      * <h3>EGL Context Lost</h3>
    672      * There are situations where the EGL rendering context will be lost. This
    673      * typically happens when device wakes up after going to sleep. When
    674      * the EGL context is lost, all OpenGL resources (such as textures) that are
    675      * associated with that context will be automatically deleted. In order to
    676      * keep rendering correctly, a renderer must recreate any lost resources
    677      * that it still needs. The {@link #onSurfaceCreated(GL10, EGLConfig)} method
    678      * is a convenient place to do this.
    679      *
    680      *
    681      * @see #setRenderer(Renderer)
    682      */
    683     public interface Renderer {
    684         /**
    685          * Called when the surface is created or recreated.
    686          * <p>
    687          * Called when the rendering thread
    688          * starts and whenever the EGL context is lost. The EGL context will typically
    689          * be lost when the Android device awakes after going to sleep.
    690          * <p>
    691          * Since this method is called at the beginning of rendering, as well as
    692          * every time the EGL context is lost, this method is a convenient place to put
    693          * code to create resources that need to be created when the rendering
    694          * starts, and that need to be recreated when the EGL context is lost.
    695          * Textures are an example of a resource that you might want to create
    696          * here.
    697          * <p>
    698          * Note that when the EGL context is lost, all OpenGL resources associated
    699          * with that context will be automatically deleted. You do not need to call
    700          * the corresponding "glDelete" methods such as glDeleteTextures to
    701          * manually delete these lost resources.
    702          * <p>
    703          * @param gl the GL interface. Use <code>instanceof</code> to
    704          * test if the interface supports GL11 or higher interfaces.
    705          * @param config the EGLConfig of the created surface. Can be used
    706          * to create matching pbuffers.
    707          */
    708         void onSurfaceCreated(GL10 gl, EGLConfig config);
    709 
    710         /**
    711          * Called when the surface changed size.
    712          * <p>
    713          * Called after the surface is created and whenever
    714          * the OpenGL ES surface size changes.
    715          * <p>
    716          * Typically you will set your viewport here. If your camera
    717          * is fixed then you could also set your projection matrix here:
    718          * <pre class="prettyprint">
    719          * void onSurfaceChanged(GL10 gl, int width, int height) {
    720          *     gl.glViewport(0, 0, width, height);
    721          *     // for a fixed camera, set the projection too
    722          *     float ratio = (float) width / height;
    723          *     gl.glMatrixMode(GL10.GL_PROJECTION);
    724          *     gl.glLoadIdentity();
    725          *     gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
    726          * }
    727          * </pre>
    728          * @param gl the GL interface. Use <code>instanceof</code> to
    729          * test if the interface supports GL11 or higher interfaces.
    730          * @param width
    731          * @param height
    732          */
    733         void onSurfaceChanged(GL10 gl, int width, int height);
    734 
    735         /**
    736          * Called to draw the current frame.
    737          * <p>
    738          * This method is responsible for drawing the current frame.
    739          * <p>
    740          * The implementation of this method typically looks like this:
    741          * <pre class="prettyprint">
    742          * void onDrawFrame(GL10 gl) {
    743          *     gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
    744          *     //... other gl calls to render the scene ...
    745          * }
    746          * </pre>
    747          * @param gl the GL interface. Use <code>instanceof</code> to
    748          * test if the interface supports GL11 or higher interfaces.
    749          */
    750         void onDrawFrame(GL10 gl);
    751     }
    752 
    753     /**
    754      * An interface for customizing the eglCreateContext and eglDestroyContext calls.
    755      * <p>
    756      * This interface must be implemented by clients wishing to call
    757      * {@link GLSurfaceView#setEGLContextFactory(EGLContextFactory)}
    758      */
    759     public interface EGLContextFactory {
    760         EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig);
    761         void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context);
    762     }
    763 
    764     private class DefaultContextFactory implements EGLContextFactory {
    765         private int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
    766 
    767         public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {
    768             int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion,
    769                     EGL10.EGL_NONE };
    770 
    771             return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT,
    772                     mEGLContextClientVersion != 0 ? attrib_list : null);
    773         }
    774 
    775         public void destroyContext(EGL10 egl, EGLDisplay display,
    776                 EGLContext context) {
    777             if (!egl.eglDestroyContext(display, context)) {
    778                 Log.e("DefaultContextFactory", "display:" + display + " context: " + context);
    779                 if (LOG_THREADS) {
    780                     Log.i("DefaultContextFactory", "tid=" + Thread.currentThread().getId());
    781                 }
    782                 EglHelper.throwEglException("eglDestroyContex", egl.eglGetError());
    783             }
    784         }
    785     }
    786 
    787     /**
    788      * An interface for customizing the eglCreateWindowSurface and eglDestroySurface calls.
    789      * <p>
    790      * This interface must be implemented by clients wishing to call
    791      * {@link GLSurfaceView#setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory)}
    792      */
    793     public interface EGLWindowSurfaceFactory {
    794         /**
    795          *  @return null if the surface cannot be constructed.
    796          */
    797         EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config,
    798                 Object nativeWindow);
    799         void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface);
    800     }
    801 
    802     private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory {
    803 
    804         public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display,
    805                 EGLConfig config, Object nativeWindow) {
    806             EGLSurface result = null;
    807             try {
    808                 result = egl.eglCreateWindowSurface(display, config, nativeWindow, null);
    809             } catch (IllegalArgumentException e) {
    810                 // This exception indicates that the surface flinger surface
    811                 // is not valid. This can happen if the surface flinger surface has
    812                 // been torn down, but the application has not yet been
    813                 // notified via SurfaceHolder.Callback.surfaceDestroyed.
    814                 // In theory the application should be notified first,
    815                 // but in practice sometimes it is not. See b/4588890
    816                 Log.e(TAG, "eglCreateWindowSurface", e);
    817             }
    818             return result;
    819         }
    820 
    821         public void destroySurface(EGL10 egl, EGLDisplay display,
    822                 EGLSurface surface) {
    823             egl.eglDestroySurface(display, surface);
    824         }
    825     }
    826 
    827     /**
    828      * An interface for choosing an EGLConfig configuration from a list of
    829      * potential configurations.
    830      * <p>
    831      * This interface must be implemented by clients wishing to call
    832      * {@link GLSurfaceView#setEGLConfigChooser(EGLConfigChooser)}
    833      */
    834     public interface EGLConfigChooser {
    835         /**
    836          * Choose a configuration from the list. Implementors typically
    837          * implement this method by calling
    838          * {@link EGL10#eglChooseConfig} and iterating through the results. Please consult the
    839          * EGL specification available from The Khronos Group to learn how to call eglChooseConfig.
    840          * @param egl the EGL10 for the current display.
    841          * @param display the current display.
    842          * @return the chosen configuration.
    843          */
    844         EGLConfig chooseConfig(EGL10 egl, EGLDisplay display);
    845     }
    846 
    847     private abstract class BaseConfigChooser
    848             implements EGLConfigChooser {
    849         public BaseConfigChooser(int[] configSpec) {
    850             mConfigSpec = filterConfigSpec(configSpec);
    851         }
    852 
    853         public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
    854             int[] num_config = new int[1];
    855             if (!egl.eglChooseConfig(display, mConfigSpec, null, 0,
    856                     num_config)) {
    857                 throw new IllegalArgumentException("eglChooseConfig failed");
    858             }
    859 
    860             int numConfigs = num_config[0];
    861 
    862             if (numConfigs <= 0) {
    863                 throw new IllegalArgumentException(
    864                         "No configs match configSpec");
    865             }
    866 
    867             EGLConfig[] configs = new EGLConfig[numConfigs];
    868             if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs,
    869                     num_config)) {
    870                 throw new IllegalArgumentException("eglChooseConfig#2 failed");
    871             }
    872             EGLConfig config = chooseConfig(egl, display, configs);
    873             if (config == null) {
    874                 throw new IllegalArgumentException("No config chosen");
    875             }
    876             return config;
    877         }
    878 
    879         abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
    880                 EGLConfig[] configs);
    881 
    882         protected int[] mConfigSpec;
    883 
    884         private int[] filterConfigSpec(int[] configSpec) {
    885             if (mEGLContextClientVersion != 2) {
    886                 return configSpec;
    887             }
    888             /* We know none of the subclasses define EGL_RENDERABLE_TYPE.
    889              * And we know the configSpec is well formed.
    890              */
    891             int len = configSpec.length;
    892             int[] newConfigSpec = new int[len + 2];
    893             System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1);
    894             newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE;
    895             newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
    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 {
    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
    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 
    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 {
    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 {
    986         public EglHelper(WeakReference<GLSurfaceView> glSurfaceViewWeakRef) {
    987             mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
    988         }
    989 
    990         /**
    991          * Initialize EGL for a given configuration spec.
    992          * @param configSpec
    993          */
    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          */
   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          */
   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          */
   1139         public int swap() {
   1140             if (! mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
   1141                 return mEgl.eglGetError();
   1142             }
   1143             return EGL10.EGL_SUCCESS;
   1144         }
   1145 
   1146         public void destroySurface() {
   1147             if (LOG_EGL) {
   1148                 Log.w("EglHelper", "destroySurface()  tid=" + Thread.currentThread().getId());
   1149             }
   1150             destroySurfaceImp();
   1151         }
   1152 
   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 
   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 
   1183         private void throwEglException(String function) {
   1184             throwEglException(function, mEgl.eglGetError());
   1185         }
   1186 
   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 
   1196         public static void logEglErrorAsWarning(String tag, String function, int error) {
   1197             Log.w(tag, formatEglError(function, error));
   1198         }
   1199 
   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 {
   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
   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          */
   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          */
   1263         private void stopEglContextLocked() {
   1264             if (mHaveEglContext) {
   1265                 mEglHelper.finish();
   1266                 mHaveEglContext = false;
   1267                 sGLThreadManager.releaseEglContextLocked(this);
   1268             }
   1269         }
   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                             view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
   1502                         }
   1503                         createEglContext = false;
   1504                     }
   1505 
   1506                     if (sizeChanged) {
   1507                         if (LOG_RENDERER) {
   1508                             Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");
   1509                         }
   1510                         GLSurfaceView view = mGLSurfaceViewWeakRef.get();
   1511                         if (view != null) {
   1512                             view.mRenderer.onSurfaceChanged(gl, w, h);
   1513                         }
   1514                         sizeChanged = false;
   1515                     }
   1516 
   1517                     if (LOG_RENDERER_DRAW_FRAME) {
   1518                         Log.w("GLThread", "onDrawFrame tid=" + getId());
   1519                     }
   1520                     {
   1521                         GLSurfaceView view = mGLSurfaceViewWeakRef.get();
   1522                         if (view != null) {
   1523                             view.mRenderer.onDrawFrame(gl);
   1524                         }
   1525                     }
   1526                     int swapError = mEglHelper.swap();
   1527                     switch (swapError) {
   1528                         case EGL10.EGL_SUCCESS:
   1529                             break;
   1530                         case EGL11.EGL_CONTEXT_LOST:
   1531                             if (LOG_SURFACE) {
   1532                                 Log.i("GLThread", "egl context lost tid=" + getId());
   1533                             }
   1534                             lostEglContext = true;
   1535                             break;
   1536                         default:
   1537                             // Other errors typically mean that the current surface is bad,
   1538                             // probably because the SurfaceView surface has been destroyed,
   1539                             // but we haven't been notified yet.
   1540                             // Log the error to help developers understand why rendering stopped.
   1541                             EglHelper.logEglErrorAsWarning("GLThread", "eglSwapBuffers", swapError);
   1542 
   1543                             synchronized(sGLThreadManager) {
   1544                                 mSurfaceIsBad = true;
   1545                                 sGLThreadManager.notifyAll();
   1546                             }
   1547                             break;
   1548                     }
   1549 
   1550                     if (wantRenderNotification) {
   1551                         doRenderNotification = true;
   1552                     }
   1553                 }
   1554 
   1555             } finally {
   1556                 /*
   1557                  * clean-up everything...
   1558                  */
   1559                 synchronized (sGLThreadManager) {
   1560                     stopEglSurfaceLocked();
   1561                     stopEglContextLocked();
   1562                 }
   1563             }
   1564         }
   1565 
   1566         public boolean ableToDraw() {
   1567             return mHaveEglContext && mHaveEglSurface && readyToDraw();
   1568         }
   1569 
   1570         private boolean readyToDraw() {
   1571             return (!mPaused) && mHasSurface && (!mSurfaceIsBad)
   1572                 && (mWidth > 0) && (mHeight > 0)
   1573                 && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY));
   1574         }
   1575 
   1576         public void setRenderMode(int renderMode) {
   1577             if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) {
   1578                 throw new IllegalArgumentException("renderMode");
   1579             }
   1580             synchronized(sGLThreadManager) {
   1581                 mRenderMode = renderMode;
   1582                 sGLThreadManager.notifyAll();
   1583             }
   1584         }
   1585 
   1586         public int getRenderMode() {
   1587             synchronized(sGLThreadManager) {
   1588                 return mRenderMode;
   1589             }
   1590         }
   1591 
   1592         public void requestRender() {
   1593             synchronized(sGLThreadManager) {
   1594                 mRequestRender = true;
   1595                 sGLThreadManager.notifyAll();
   1596             }
   1597         }
   1598 
   1599         public void surfaceCreated() {
   1600             synchronized(sGLThreadManager) {
   1601                 if (LOG_THREADS) {
   1602                     Log.i("GLThread", "surfaceCreated tid=" + getId());
   1603                 }
   1604                 mHasSurface = true;
   1605                 mFinishedCreatingEglSurface = false;
   1606                 sGLThreadManager.notifyAll();
   1607                 while (mWaitingForSurface
   1608                        && !mFinishedCreatingEglSurface
   1609                        && !mExited) {
   1610                     try {
   1611                         sGLThreadManager.wait();
   1612                     } catch (InterruptedException e) {
   1613                         Thread.currentThread().interrupt();
   1614                     }
   1615                 }
   1616             }
   1617         }
   1618 
   1619         public void surfaceDestroyed() {
   1620             synchronized(sGLThreadManager) {
   1621                 if (LOG_THREADS) {
   1622                     Log.i("GLThread", "surfaceDestroyed tid=" + getId());
   1623                 }
   1624                 mHasSurface = false;
   1625                 sGLThreadManager.notifyAll();
   1626                 while((!mWaitingForSurface) && (!mExited)) {
   1627                     try {
   1628                         sGLThreadManager.wait();
   1629                     } catch (InterruptedException e) {
   1630                         Thread.currentThread().interrupt();
   1631                     }
   1632                 }
   1633             }
   1634         }
   1635 
   1636         public void onPause() {
   1637             synchronized (sGLThreadManager) {
   1638                 if (LOG_PAUSE_RESUME) {
   1639                     Log.i("GLThread", "onPause tid=" + getId());
   1640                 }
   1641                 mRequestPaused = true;
   1642                 sGLThreadManager.notifyAll();
   1643                 while ((! mExited) && (! mPaused)) {
   1644                     if (LOG_PAUSE_RESUME) {
   1645                         Log.i("Main thread", "onPause waiting for mPaused.");
   1646                     }
   1647                     try {
   1648                         sGLThreadManager.wait();
   1649                     } catch (InterruptedException ex) {
   1650                         Thread.currentThread().interrupt();
   1651                     }
   1652                 }
   1653             }
   1654         }
   1655 
   1656         public void onResume() {
   1657             synchronized (sGLThreadManager) {
   1658                 if (LOG_PAUSE_RESUME) {
   1659                     Log.i("GLThread", "onResume tid=" + getId());
   1660                 }
   1661                 mRequestPaused = false;
   1662                 mRequestRender = true;
   1663                 mRenderComplete = false;
   1664                 sGLThreadManager.notifyAll();
   1665                 while ((! mExited) && mPaused && (!mRenderComplete)) {
   1666                     if (LOG_PAUSE_RESUME) {
   1667                         Log.i("Main thread", "onResume waiting for !mPaused.");
   1668                     }
   1669                     try {
   1670                         sGLThreadManager.wait();
   1671                     } catch (InterruptedException ex) {
   1672                         Thread.currentThread().interrupt();
   1673                     }
   1674                 }
   1675             }
   1676         }
   1677 
   1678         public void onWindowResize(int w, int h) {
   1679             synchronized (sGLThreadManager) {
   1680                 mWidth = w;
   1681                 mHeight = h;
   1682                 mSizeChanged = true;
   1683                 mRequestRender = true;
   1684                 mRenderComplete = false;
   1685                 sGLThreadManager.notifyAll();
   1686 
   1687                 // Wait for thread to react to resize and render a frame
   1688                 while (! mExited && !mPaused && !mRenderComplete
   1689                         && ableToDraw()) {
   1690                     if (LOG_SURFACE) {
   1691                         Log.i("Main thread", "onWindowResize waiting for render complete from tid=" + getId());
   1692                     }
   1693                     try {
   1694                         sGLThreadManager.wait();
   1695                     } catch (InterruptedException ex) {
   1696                         Thread.currentThread().interrupt();
   1697                     }
   1698                 }
   1699             }
   1700         }
   1701 
   1702         public void requestExitAndWait() {
   1703             // don't call this from GLThread thread or it is a guaranteed
   1704             // deadlock!
   1705             synchronized(sGLThreadManager) {
   1706                 mShouldExit = true;
   1707                 sGLThreadManager.notifyAll();
   1708                 while (! mExited) {
   1709                     try {
   1710                         sGLThreadManager.wait();
   1711                     } catch (InterruptedException ex) {
   1712                         Thread.currentThread().interrupt();
   1713                     }
   1714                 }
   1715             }
   1716         }
   1717 
   1718         public void requestReleaseEglContextLocked() {
   1719             mShouldReleaseEglContext = true;
   1720             sGLThreadManager.notifyAll();
   1721         }
   1722 
   1723         /**
   1724          * Queue an "event" to be run on the GL rendering thread.
   1725          * @param r the runnable to be run on the GL rendering thread.
   1726          */
   1727         public void queueEvent(Runnable r) {
   1728             if (r == null) {
   1729                 throw new IllegalArgumentException("r must not be null");
   1730             }
   1731             synchronized(sGLThreadManager) {
   1732                 mEventQueue.add(r);
   1733                 sGLThreadManager.notifyAll();
   1734             }
   1735         }
   1736 
   1737         // Once the thread is started, all accesses to the following member
   1738         // variables are protected by the sGLThreadManager monitor
   1739         private boolean mShouldExit;
   1740         private boolean mExited;
   1741         private boolean mRequestPaused;
   1742         private boolean mPaused;
   1743         private boolean mHasSurface;
   1744         private boolean mSurfaceIsBad;
   1745         private boolean mWaitingForSurface;
   1746         private boolean mHaveEglContext;
   1747         private boolean mHaveEglSurface;
   1748         private boolean mFinishedCreatingEglSurface;
   1749         private boolean mShouldReleaseEglContext;
   1750         private int mWidth;
   1751         private int mHeight;
   1752         private int mRenderMode;
   1753         private boolean mRequestRender;
   1754         private boolean mRenderComplete;
   1755         private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>();
   1756         private boolean mSizeChanged = true;
   1757 
   1758         // End of member variables protected by the sGLThreadManager monitor.
   1759 
   1760         private EglHelper mEglHelper;
   1761 
   1762         /**
   1763          * Set once at thread construction time, nulled out when the parent view is garbage
   1764          * called. This weak reference allows the GLSurfaceView to be garbage collected while
   1765          * the GLThread is still alive.
   1766          */
   1767         private WeakReference<GLSurfaceView> mGLSurfaceViewWeakRef;
   1768 
   1769     }
   1770 
   1771     static class LogWriter extends Writer {
   1772 
   1773         @Override public void close() {
   1774             flushBuilder();
   1775         }
   1776 
   1777         @Override public void flush() {
   1778             flushBuilder();
   1779         }
   1780 
   1781         @Override public void write(char[] buf, int offset, int count) {
   1782             for(int i = 0; i < count; i++) {
   1783                 char c = buf[offset + i];
   1784                 if ( c == '\n') {
   1785                     flushBuilder();
   1786                 }
   1787                 else {
   1788                     mBuilder.append(c);
   1789                 }
   1790             }
   1791         }
   1792 
   1793         private void flushBuilder() {
   1794             if (mBuilder.length() > 0) {
   1795                 Log.v("GLSurfaceView", mBuilder.toString());
   1796                 mBuilder.delete(0, mBuilder.length());
   1797             }
   1798         }
   1799 
   1800         private StringBuilder mBuilder = new StringBuilder();
   1801     }
   1802 
   1803 
   1804     private void checkRenderThreadState() {
   1805         if (mGLThread != null) {
   1806             throw new IllegalStateException(
   1807                     "setRenderer has already been called for this instance.");
   1808         }
   1809     }
   1810 
   1811     private static class GLThreadManager {
   1812         private static String TAG = "GLThreadManager";
   1813 
   1814         public synchronized void threadExiting(GLThread thread) {
   1815             if (LOG_THREADS) {
   1816                 Log.i("GLThread", "exiting tid=" +  thread.getId());
   1817             }
   1818             thread.mExited = true;
   1819             if (mEglOwner == thread) {
   1820                 mEglOwner = null;
   1821             }
   1822             notifyAll();
   1823         }
   1824 
   1825         /*
   1826          * Tries once to acquire the right to use an EGL
   1827          * context. Does not block. Requires that we are already
   1828          * in the sGLThreadManager monitor when this is called.
   1829          *
   1830          * @return true if the right to use an EGL context was acquired.
   1831          */
   1832         public boolean tryAcquireEglContextLocked(GLThread thread) {
   1833             if (mEglOwner == thread || mEglOwner == null) {
   1834                 mEglOwner = thread;
   1835                 notifyAll();
   1836                 return true;
   1837             }
   1838             checkGLESVersion();
   1839             if (mMultipleGLESContextsAllowed) {
   1840                 return true;
   1841             }
   1842             // Notify the owning thread that it should release the context.
   1843             // TODO: implement a fairness policy. Currently
   1844             // if the owning thread is drawing continuously it will just
   1845             // reacquire the EGL context.
   1846             if (mEglOwner != null) {
   1847                 mEglOwner.requestReleaseEglContextLocked();
   1848             }
   1849             return false;
   1850         }
   1851 
   1852         /*
   1853          * Releases the EGL context. Requires that we are already in the
   1854          * sGLThreadManager monitor when this is called.
   1855          */
   1856         public void releaseEglContextLocked(GLThread thread) {
   1857             if (mEglOwner == thread) {
   1858                 mEglOwner = null;
   1859             }
   1860             notifyAll();
   1861         }
   1862 
   1863         public synchronized boolean shouldReleaseEGLContextWhenPausing() {
   1864             // Release the EGL context when pausing even if
   1865             // the hardware supports multiple EGL contexts.
   1866             // Otherwise the device could run out of EGL contexts.
   1867             return mLimitedGLESContexts;
   1868         }
   1869 
   1870         public synchronized boolean shouldTerminateEGLWhenPausing() {
   1871             checkGLESVersion();
   1872             return !mMultipleGLESContextsAllowed;
   1873         }
   1874 
   1875         public synchronized void checkGLDriver(GL10 gl) {
   1876             if (! mGLESDriverCheckComplete) {
   1877                 checkGLESVersion();
   1878                 String renderer = gl.glGetString(GL10.GL_RENDERER);
   1879                 if (mGLESVersion < kGLES_20) {
   1880                     mMultipleGLESContextsAllowed =
   1881                         ! renderer.startsWith(kMSM7K_RENDERER_PREFIX);
   1882                     notifyAll();
   1883                 }
   1884                 mLimitedGLESContexts = !mMultipleGLESContextsAllowed;
   1885                 if (LOG_SURFACE) {
   1886                     Log.w(TAG, "checkGLDriver renderer = \"" + renderer + "\" multipleContextsAllowed = "
   1887                         + mMultipleGLESContextsAllowed
   1888                         + " mLimitedGLESContexts = " + mLimitedGLESContexts);
   1889                 }
   1890                 mGLESDriverCheckComplete = true;
   1891             }
   1892         }
   1893 
   1894         private void checkGLESVersion() {
   1895             if (! mGLESVersionCheckComplete) {
   1896                 mGLESVersion = SystemProperties.getInt(
   1897                         "ro.opengles.version",
   1898                         ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
   1899                 if (mGLESVersion >= kGLES_20) {
   1900                     mMultipleGLESContextsAllowed = true;
   1901                 }
   1902                 if (LOG_SURFACE) {
   1903                     Log.w(TAG, "checkGLESVersion mGLESVersion =" +
   1904                             " " + mGLESVersion + " mMultipleGLESContextsAllowed = " + mMultipleGLESContextsAllowed);
   1905                 }
   1906                 mGLESVersionCheckComplete = true;
   1907             }
   1908         }
   1909 
   1910         /**
   1911          * This check was required for some pre-Android-3.0 hardware. Android 3.0 provides
   1912          * support for hardware-accelerated views, therefore multiple EGL contexts are
   1913          * supported on all Android 3.0+ EGL drivers.
   1914          */
   1915         private boolean mGLESVersionCheckComplete;
   1916         private int mGLESVersion;
   1917         private boolean mGLESDriverCheckComplete;
   1918         private boolean mMultipleGLESContextsAllowed;
   1919         private boolean mLimitedGLESContexts;
   1920         private static final int kGLES_20 = 0x20000;
   1921         private static final String kMSM7K_RENDERER_PREFIX =
   1922             "Q3Dimension MSM7500 ";
   1923         private GLThread mEglOwner;
   1924     }
   1925 
   1926     private static final GLThreadManager sGLThreadManager = new GLThreadManager();
   1927 
   1928     private final WeakReference<GLSurfaceView> mThisWeakRef =
   1929             new WeakReference<GLSurfaceView>(this);
   1930     private GLThread mGLThread;
   1931     private Renderer mRenderer;
   1932     private boolean mDetached;
   1933     private EGLConfigChooser mEGLConfigChooser;
   1934     private EGLContextFactory mEGLContextFactory;
   1935     private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;
   1936     private GLWrapper mGLWrapper;
   1937     private int mDebugFlags;
   1938     private int mEGLContextClientVersion;
   1939     private boolean mPreserveEGLContextOnPause;
   1940 }
   1941