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