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