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