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