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