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