2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.opengl;
19 import java.io.Writer;
20 import java.util.ArrayList;
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;
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;
41 * An implementation of SurfaceView that uses the dedicated surface for
42 * displaying OpenGL rendering.
44 * A GLSurfaceView provides the following features:
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
53 * <li>Supports both on-demand and continuous rendering.
54 * <li>Optionally wraps, traces, and/or error-checks the renderer's OpenGL calls.
57 * <h3>Using GLSurfaceView</h3>
59 * Typically you use GLSurfaceView by subclassing it and overriding one or more of the
60 * View system input event methods. If your application does not need to override event
61 * methods then GLSurfaceView can be used as-is. For the most part
62 * GLSurfaceView behavior is customized by calling "set" methods rather than by subclassing.
63 * For example, unlike a regular View, drawing is delegated to a separate Renderer object which
64 * is registered with the GLSurfaceView
65 * using the {@link #setRenderer(Renderer)} call.
67 * <h3>Initializing GLSurfaceView</h3>
68 * All you have to do to initialize a GLSurfaceView is call {@link #setRenderer(Renderer)}.
69 * However, if desired, you can modify the default behavior of GLSurfaceView by calling one or
70 * more of these methods before calling setRenderer:
72 * <li>{@link #setDebugFlags(int)}
73 * <li>{@link #setEGLConfigChooser(boolean)}
74 * <li>{@link #setEGLConfigChooser(EGLConfigChooser)}
75 * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)}
76 * <li>{@link #setGLWrapper(GLWrapper)}
79 * <h4>Specifying the android.view.Surface</h4>
80 * By default GLSurfaceView will create a PixelFormat.RGB_565 format surface. If a translucent
81 * surface is required, call getHolder().setFormat(PixelFormat.TRANSLUCENT).
82 * The exact format of a TRANSLUCENT surface is device dependent, but it will be
83 * a 32-bit-per-pixel surface with 8 bits per component.
85 * <h4>Choosing an EGL Configuration</h4>
86 * A given Android device may support multiple EGLConfig rendering configurations.
87 * The available configurations may differ in how may channels of data are present, as
88 * well as how many bits are allocated to each channel. Therefore, the first thing
89 * GLSurfaceView has to do when starting to render is choose what EGLConfig to use.
91 * By default GLSurfaceView chooses a EGLConfig that has an RGB_656 pixel format,
92 * with at least a 16-bit depth buffer and no stencil.
94 * If you would prefer a different EGLConfig
95 * you can override the default behavior by calling one of the
96 * setEGLConfigChooser methods.
98 * <h4>Debug Behavior</h4>
99 * You can optionally modify the behavior of GLSurfaceView by calling
100 * one or more of the debugging methods {@link #setDebugFlags(int)},
101 * and {@link #setGLWrapper}. These methods may be called before and/or after setRenderer, but
102 * typically they are called before setRenderer so that they take effect immediately.
104 * <h4>Setting a Renderer</h4>
105 * Finally, you must call {@link #setRenderer} to register a {@link Renderer}.
107 * responsible for doing the actual OpenGL rendering.
109 * <h3>Rendering Mode</h3>
110 * Once the renderer is set, you can control whether the renderer draws
111 * continuously or on-demand by calling
112 * {@link #setRenderMode}. The default is continuous rendering.
114 * <h3>Activity Life-cycle</h3>
115 * A GLSurfaceView must be notified when the activity is paused and resumed. GLSurfaceView clients
116 * are required to call {@link #onPause()} when the activity pauses and
117 * {@link #onResume()} when the activity resumes. These calls allow GLSurfaceView to
118 * pause and resume the rendering thread, and also allow GLSurfaceView to release and recreate
119 * the OpenGL display.
121 * <h3>Handling events</h3>
123 * To handle an event you will typically subclass GLSurfaceView and override the
124 * appropriate method, just as you would with any other View. However, when handling
125 * the event, you may need to communicate with the Renderer object
126 * that's running in the rendering thread. You can do this using any
127 * standard Java cross-thread communication mechanism. In addition,
128 * one relatively easy way to communicate with your renderer is
130 * {@link #queueEvent(Runnable)}. For example:
131 * <pre class="prettyprint">
132 * class MyGLSurfaceView extends GLSurfaceView {
134 * private MyRenderer mMyRenderer;
136 * public void start() {
138 * setRenderer(mMyRenderer);
141 * public boolean onKeyDown(int keyCode, KeyEvent event) {
142 * if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
143 * queueEvent(new Runnable() {
144 * // This method will be called on the rendering
146 * public void run() {
147 * mMyRenderer.handleDpadCenter();
151 * return super.onKeyDown(keyCode, event);
157 public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
158 private final static boolean LOG_THREADS = false;
159 private final static boolean LOG_PAUSE_RESUME = false;
160 private final static boolean LOG_SURFACE = false;
161 private final static boolean LOG_RENDERER = false;
162 private final static boolean LOG_RENDERER_DRAW_FRAME = false;
163 private final static boolean LOG_EGL = false;
164 // Work-around for bug 2263168
165 private final static boolean DRAW_TWICE_AFTER_SIZE_CHANGED = true;
167 * The renderer only renders
168 * when the surface is created, or when {@link #requestRender} is called.
170 * @see #getRenderMode()
171 * @see #setRenderMode(int)
172 * @see #requestRender()
174 public final static int RENDERMODE_WHEN_DIRTY = 0;
176 * The renderer is called
177 * continuously to re-render the scene.
179 * @see #getRenderMode()
180 * @see #setRenderMode(int)
182 public final static int RENDERMODE_CONTINUOUSLY = 1;
185 * Check glError() after every GL call and throw an exception if glError indicates
186 * that an error has occurred. This can be used to help track down which OpenGL ES call
187 * is causing an error.
189 * @see #getDebugFlags
190 * @see #setDebugFlags
192 public final static int DEBUG_CHECK_GL_ERROR = 1;
195 * Log GL calls to the system log at "verbose" level with tag "GLSurfaceView".
197 * @see #getDebugFlags
198 * @see #setDebugFlags
200 public final static int DEBUG_LOG_GL_CALLS = 2;
203 * Standard View constructor. In order to render something, you
204 * must call {@link #setRenderer} to register a renderer.
206 public GLSurfaceView(Context context) {
212 * Standard View constructor. In order to render something, you
213 * must call {@link #setRenderer} to register a renderer.
215 public GLSurfaceView(Context context, AttributeSet attrs) {
216 super(context, attrs);
220 private void init() {
221 // Install a SurfaceHolder.Callback so we get notified when the
222 // underlying surface is created and destroyed
223 SurfaceHolder holder = getHolder();
224 holder.addCallback(this);
225 // setFormat is done by SurfaceView in SDK 2.3 and newer. Uncomment
226 // this statement if back-porting to 2.2 or older:
227 // holder.setFormat(PixelFormat.RGB_565);
229 // setType is not needed for SDK 2.0 or newer. Uncomment this
230 // statement if back-porting this code to older SDKs.
231 // holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
235 * Set the glWrapper. If the glWrapper is not null, its
236 * {@link GLWrapper#wrap(GL)} method is called
237 * whenever a surface is created. A GLWrapper can be used to wrap
238 * the GL object that's passed to the renderer. Wrapping a GL
239 * object enables examining and modifying the behavior of the
240 * GL calls made by the renderer.
242 * Wrapping is typically used for debugging purposes.
244 * The default value is null.
245 * @param glWrapper the new GLWrapper
247 public void setGLWrapper(GLWrapper glWrapper) {
248 mGLWrapper = glWrapper;
252 * Set the debug flags to a new value. The value is
253 * constructed by OR-together zero or more
254 * of the DEBUG_CHECK_* constants. The debug flags take effect
255 * whenever a surface is created. The default value is zero.
256 * @param debugFlags the new debug flags
257 * @see #DEBUG_CHECK_GL_ERROR
258 * @see #DEBUG_LOG_GL_CALLS
260 public void setDebugFlags(int debugFlags) {
261 mDebugFlags = debugFlags;
265 * Get the current value of the debug flags.
266 * @return the current value of the debug flags.
268 public int getDebugFlags() {
273 * Set the renderer associated with this view. Also starts the thread that
274 * will call the renderer, which in turn causes the rendering to start.
275 * <p>This method should be called once and only once in the life-cycle of
277 * <p>The following GLSurfaceView methods can only be called <em>before</em>
278 * setRenderer is called:
280 * <li>{@link #setEGLConfigChooser(boolean)}
281 * <li>{@link #setEGLConfigChooser(EGLConfigChooser)}
282 * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)}
285 * The following GLSurfaceView methods can only be called <em>after</em>
286 * setRenderer is called:
288 * <li>{@link #getRenderMode()}
289 * <li>{@link #onPause()}
290 * <li>{@link #onResume()}
291 * <li>{@link #queueEvent(Runnable)}
292 * <li>{@link #requestRender()}
293 * <li>{@link #setRenderMode(int)}
296 * @param renderer the renderer to use to perform OpenGL drawing.
298 public void setRenderer(Renderer renderer) {
299 checkRenderThreadState();
300 if (mEGLConfigChooser == null) {
301 mEGLConfigChooser = new SimpleEGLConfigChooser(true);
303 if (mEGLContextFactory == null) {
304 mEGLContextFactory = new DefaultContextFactory();
306 if (mEGLWindowSurfaceFactory == null) {
307 mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
309 mGLThread = new GLThread(renderer);
314 * Install a custom EGLContextFactory.
315 * <p>If this method is
316 * called, it must be called before {@link #setRenderer(Renderer)}
319 * If this method is not called, then by default
320 * a context will be created with no shared context and
321 * with a null attribute list.
323 public void setEGLContextFactory(EGLContextFactory factory) {
324 checkRenderThreadState();
325 mEGLContextFactory = factory;
329 * Install a custom EGLWindowSurfaceFactory.
330 * <p>If this method is
331 * called, it must be called before {@link #setRenderer(Renderer)}
334 * If this method is not called, then by default
335 * a window surface will be created with a null attribute list.
337 public void setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory factory) {
338 checkRenderThreadState();
339 mEGLWindowSurfaceFactory = factory;
343 * Install a custom EGLConfigChooser.
344 * <p>If this method is
345 * called, it must be called before {@link #setRenderer(Renderer)}
348 * If no setEGLConfigChooser method is called, then by default the
349 * view will choose an EGLConfig that is compatible with the current
350 * android.view.Surface, with a depth buffer depth of
352 * @param configChooser
354 public void setEGLConfigChooser(EGLConfigChooser configChooser) {
355 checkRenderThreadState();
356 mEGLConfigChooser = configChooser;
360 * Install a config chooser which will choose a config
361 * as close to 16-bit RGB as possible, with or without an optional depth
362 * buffer as close to 16-bits as possible.
363 * <p>If this method is
364 * called, it must be called before {@link #setRenderer(Renderer)}
367 * If no setEGLConfigChooser method is called, then by default the
368 * view will choose an RGB_565 surface with a depth buffer depth of
373 public void setEGLConfigChooser(boolean needDepth) {
374 setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth));
378 * Install a config chooser which will choose a config
379 * with at least the specified depthSize and stencilSize,
380 * and exactly the specified redSize, greenSize, blueSize and alphaSize.
381 * <p>If this method is
382 * called, it must be called before {@link #setRenderer(Renderer)}
385 * If no setEGLConfigChooser method is called, then by default the
386 * view will choose an RGB_565 surface with a depth buffer depth of
390 public void setEGLConfigChooser(int redSize, int greenSize, int blueSize,
391 int alphaSize, int depthSize, int stencilSize) {
392 setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize,
393 blueSize, alphaSize, depthSize, stencilSize));
397 * Inform the default EGLContextFactory and default EGLConfigChooser
398 * which EGLContext client version to pick.
399 * <p>Use this method to create an OpenGL ES 2.0-compatible context.
401 * <pre class="prettyprint">
402 * public MyView(Context context) {
404 * setEGLContextClientVersion(2); // Pick an OpenGL ES 2.0 context.
405 * setRenderer(new MyRenderer());
408 * <p>Note: Activities which require OpenGL ES 2.0 should indicate this by
409 * setting @lt;uses-feature android:glEsVersion="0x00020000" /> in the activity's
410 * AndroidManifest.xml file.
411 * <p>If this method is called, it must be called before {@link #setRenderer(Renderer)}
413 * <p>This method only affects the behavior of the default EGLContexFactory and the
414 * default EGLConfigChooser. If
415 * {@link #setEGLContextFactory(EGLContextFactory)} has been called, then the supplied
416 * EGLContextFactory is responsible for creating an OpenGL ES 2.0-compatible context.
418 * {@link #setEGLConfigChooser(EGLConfigChooser)} has been called, then the supplied
419 * EGLConfigChooser is responsible for choosing an OpenGL ES 2.0-compatible config.
420 * @param version The EGLContext client version to choose. Use 2 for OpenGL ES 2.0
422 public void setEGLContextClientVersion(int version) {
423 checkRenderThreadState();
424 mEGLContextClientVersion = version;
428 * Set the rendering mode. When renderMode is
429 * RENDERMODE_CONTINUOUSLY, the renderer is called
430 * repeatedly to re-render the scene. When renderMode
431 * is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface
432 * is created, or when {@link #requestRender} is called. Defaults to RENDERMODE_CONTINUOUSLY.
434 * Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system performance
435 * by allowing the GPU and CPU to idle when the view does not need to be updated.
437 * This method can only be called after {@link #setRenderer(Renderer)}
439 * @param renderMode one of the RENDERMODE_X constants
440 * @see #RENDERMODE_CONTINUOUSLY
441 * @see #RENDERMODE_WHEN_DIRTY
443 public void setRenderMode(int renderMode) {
444 mGLThread.setRenderMode(renderMode);
448 * Get the current rendering mode. May be called
449 * from any thread. Must not be called before a renderer has been set.
450 * @return the current rendering mode.
451 * @see #RENDERMODE_CONTINUOUSLY
452 * @see #RENDERMODE_WHEN_DIRTY
454 public int getRenderMode() {
455 return mGLThread.getRenderMode();
459 * Request that the renderer render a frame.
460 * This method is typically used when the render mode has been set to
461 * {@link #RENDERMODE_WHEN_DIRTY}, so that frames are only rendered on demand.
463 * from any thread. Must not be called before a renderer has been set.
465 public void requestRender() {
466 mGLThread.requestRender();
470 * This method is part of the SurfaceHolder.Callback interface, and is
471 * not normally called or subclassed by clients of GLSurfaceView.
473 public void surfaceCreated(SurfaceHolder holder) {
474 mGLThread.surfaceCreated();
478 * This method is part of the SurfaceHolder.Callback interface, and is
479 * not normally called or subclassed by clients of GLSurfaceView.
481 public void surfaceDestroyed(SurfaceHolder holder) {
482 // Surface will be destroyed when we return
483 mGLThread.surfaceDestroyed();
487 * This method is part of the SurfaceHolder.Callback interface, and is
488 * not normally called or subclassed by clients of GLSurfaceView.
490 public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
491 mGLThread.onWindowResize(w, h);
495 * Inform the view that the activity is paused. The owner of this view must
496 * call this method when the activity is paused. Calling this method will
497 * pause the rendering thread.
498 * Must not be called before a renderer has been set.
500 public void onPause() {
505 * Inform the view that the activity is resumed. The owner of this view must
506 * call this method when the activity is resumed. Calling this method will
507 * recreate the OpenGL display and resume the rendering
509 * Must not be called before a renderer has been set.
511 public void onResume() {
512 mGLThread.onResume();
516 * Queue a runnable to be run on the GL rendering thread. This can be used
517 * to communicate with the Renderer on the rendering thread.
518 * Must not be called before a renderer has been set.
519 * @param r the runnable to be run on the GL rendering thread.
521 public void queueEvent(Runnable r) {
522 mGLThread.queueEvent(r);
526 * This method is used as part of the View class and is not normally
527 * called or subclassed by clients of GLSurfaceView.
528 * Must not be called before a renderer has been set.
531 protected void onDetachedFromWindow() {
532 super.onDetachedFromWindow();
533 mGLThread.requestExitAndWait();
536 // ----------------------------------------------------------------------
539 * An interface used to wrap a GL interface.
541 * used for implementing debugging and tracing on top of the default
542 * GL interface. You would typically use this by creating your own class
543 * that implemented all the GL methods by delegating to another GL instance.
544 * Then you could add your own behavior before or after calling the
545 * delegate. All the GLWrapper would do was instantiate and return the
546 * wrapper GL instance:
547 * <pre class="prettyprint">
548 * class MyGLWrapper implements GLWrapper {
550 * return new MyGLImplementation(gl);
552 * static class MyGLImplementation implements GL,GL10,GL11,... {
557 * @see #setGLWrapper(GLWrapper)
559 public interface GLWrapper {
561 * Wraps a gl interface in another gl interface.
562 * @param gl a GL interface that is to be wrapped.
563 * @return either the input argument or another GL object that wraps the input argument.
569 * A generic renderer interface.
571 * The renderer is responsible for making OpenGL calls to render a frame.
573 * GLSurfaceView clients typically create their own classes that implement
574 * this interface, and then call {@link GLSurfaceView#setRenderer} to
575 * register the renderer with the GLSurfaceView.
578 * The renderer will be called on a separate thread, so that rendering
579 * performance is decoupled from the UI thread. Clients typically need to
580 * communicate with the renderer from the UI thread, because that's where
581 * input events are received. Clients can communicate using any of the
582 * standard Java techniques for cross-thread communication, or they can
583 * use the {@link GLSurfaceView#queueEvent(Runnable)} convenience method.
585 * <h3>EGL Context Lost</h3>
586 * There are situations where the EGL rendering context will be lost. This
587 * typically happens when device wakes up after going to sleep. When
588 * the EGL context is lost, all OpenGL resources (such as textures) that are
589 * associated with that context will be automatically deleted. In order to
590 * keep rendering correctly, a renderer must recreate any lost resources
591 * that it still needs. The {@link #onSurfaceCreated(GL10, EGLConfig)} method
592 * is a convenient place to do this.
595 * @see #setRenderer(Renderer)
597 public interface Renderer {
599 * Called when the surface is created or recreated.
601 * Called when the rendering thread
602 * starts and whenever the EGL context is lost. The EGL context will typically
603 * be lost when the Android device awakes after going to sleep.
605 * Since this method is called at the beginning of rendering, as well as
606 * every time the EGL context is lost, this method is a convenient place to put
607 * code to create resources that need to be created when the rendering
608 * starts, and that need to be recreated when the EGL context is lost.
609 * Textures are an example of a resource that you might want to create
612 * Note that when the EGL context is lost, all OpenGL resources associated
613 * with that context will be automatically deleted. You do not need to call
614 * the corresponding "glDelete" methods such as glDeleteTextures to
615 * manually delete these lost resources.
617 * @param gl the GL interface. Use <code>instanceof</code> to
618 * test if the interface supports GL11 or higher interfaces.
619 * @param config the EGLConfig of the created surface. Can be used
620 * to create matching pbuffers.
622 void onSurfaceCreated(GL10 gl, EGLConfig config);
625 * Called when the surface changed size.
627 * Called after the surface is created and whenever
628 * the OpenGL ES surface size changes.
630 * Typically you will set your viewport here. If your camera
631 * is fixed then you could also set your projection matrix here:
632 * <pre class="prettyprint">
633 * void onSurfaceChanged(GL10 gl, int width, int height) {
634 * gl.glViewport(0, 0, width, height);
635 * // for a fixed camera, set the projection too
636 * float ratio = (float) width / height;
637 * gl.glMatrixMode(GL10.GL_PROJECTION);
638 * gl.glLoadIdentity();
639 * gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
642 * @param gl the GL interface. Use <code>instanceof</code> to
643 * test if the interface supports GL11 or higher interfaces.
647 void onSurfaceChanged(GL10 gl, int width, int height);
650 * Called to draw the current frame.
652 * This method is responsible for drawing the current frame.
654 * The implementation of this method typically looks like this:
655 * <pre class="prettyprint">
656 * void onDrawFrame(GL10 gl) {
657 * gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
658 * //... other gl calls to render the scene ...
661 * @param gl the GL interface. Use <code>instanceof</code> to
662 * test if the interface supports GL11 or higher interfaces.
664 void onDrawFrame(GL10 gl);
668 * An interface for customizing the eglCreateContext and eglDestroyContext calls.
670 * This interface must be implemented by clients wishing to call
671 * {@link GLSurfaceView#setEGLContextFactory(EGLContextFactory)}
673 public interface EGLContextFactory {
674 EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig);
675 void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context);
678 private class DefaultContextFactory implements EGLContextFactory {
679 private int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
681 public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {
682 int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion,
685 return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT,
686 mEGLContextClientVersion != 0 ? attrib_list : null);
689 public void destroyContext(EGL10 egl, EGLDisplay display,
690 EGLContext context) {
691 if (!egl.eglDestroyContext(display, context)) {
692 Log.e("DefaultContextFactory", "display:" + display + " context: " + context);
694 Log.i("DefaultContextFactory", "tid=" + Thread.currentThread().getId());
696 throw new RuntimeException("eglDestroyContext failed: "
697 + EGLLogWrapper.getErrorString(egl.eglGetError()));
703 * An interface for customizing the eglCreateWindowSurface and eglDestroySurface calls.
705 * This interface must be implemented by clients wishing to call
706 * {@link GLSurfaceView#setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory)}
708 public interface EGLWindowSurfaceFactory {
709 EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config,
710 Object nativeWindow);
711 void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface);
714 private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory {
716 public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display,
717 EGLConfig config, Object nativeWindow) {
718 return egl.eglCreateWindowSurface(display, config, nativeWindow, null);
721 public void destroySurface(EGL10 egl, EGLDisplay display,
722 EGLSurface surface) {
723 egl.eglDestroySurface(display, surface);
728 * An interface for choosing an EGLConfig configuration from a list of
729 * potential configurations.
731 * This interface must be implemented by clients wishing to call
732 * {@link GLSurfaceView#setEGLConfigChooser(EGLConfigChooser)}
734 public interface EGLConfigChooser {
736 * Choose a configuration from the list. Implementors typically
737 * implement this method by calling
738 * {@link EGL10#eglChooseConfig} and iterating through the results. Please consult the
739 * EGL specification available from The Khronos Group to learn how to call eglChooseConfig.
740 * @param egl the EGL10 for the current display.
741 * @param display the current display.
742 * @return the chosen configuration.
744 EGLConfig chooseConfig(EGL10 egl, EGLDisplay display);
747 private abstract class BaseConfigChooser
748 implements EGLConfigChooser {
749 public BaseConfigChooser(int[] configSpec) {
750 mConfigSpec = filterConfigSpec(configSpec);
753 public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
754 int[] num_config = new int[1];
755 if (!egl.eglChooseConfig(display, mConfigSpec, null, 0,
757 throw new IllegalArgumentException("eglChooseConfig failed");
760 int numConfigs = num_config[0];
762 if (numConfigs <= 0) {
763 throw new IllegalArgumentException(
764 "No configs match configSpec");
767 EGLConfig[] configs = new EGLConfig[numConfigs];
768 if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs,
770 throw new IllegalArgumentException("eglChooseConfig#2 failed");
772 EGLConfig config = chooseConfig(egl, display, configs);
773 if (config == null) {
774 throw new IllegalArgumentException("No config chosen");
779 abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
780 EGLConfig[] configs);
782 protected int[] mConfigSpec;
784 private int[] filterConfigSpec(int[] configSpec) {
785 if (mEGLContextClientVersion != 2) {
788 /* We know none of the subclasses define EGL_RENDERABLE_TYPE.
789 * And we know the configSpec is well formed.
791 int len = configSpec.length;
792 int[] newConfigSpec = new int[len + 2];
793 System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1);
794 newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE;
795 newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
796 newConfigSpec[len+1] = EGL10.EGL_NONE;
797 return newConfigSpec;
802 * Choose a configuration with exactly the specified r,g,b,a sizes,
803 * and at least the specified depth and stencil sizes.
805 private class ComponentSizeChooser extends BaseConfigChooser {
806 public ComponentSizeChooser(int redSize, int greenSize, int blueSize,
807 int alphaSize, int depthSize, int stencilSize) {
809 EGL10.EGL_RED_SIZE, redSize,
810 EGL10.EGL_GREEN_SIZE, greenSize,
811 EGL10.EGL_BLUE_SIZE, blueSize,
812 EGL10.EGL_ALPHA_SIZE, alphaSize,
813 EGL10.EGL_DEPTH_SIZE, depthSize,
814 EGL10.EGL_STENCIL_SIZE, stencilSize,
818 mGreenSize = greenSize;
819 mBlueSize = blueSize;
820 mAlphaSize = alphaSize;
821 mDepthSize = depthSize;
822 mStencilSize = stencilSize;
826 public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
827 EGLConfig[] configs) {
828 for (EGLConfig config : configs) {
829 int d = findConfigAttrib(egl, display, config,
830 EGL10.EGL_DEPTH_SIZE, 0);
831 int s = findConfigAttrib(egl, display, config,
832 EGL10.EGL_STENCIL_SIZE, 0);
833 if ((d >= mDepthSize) && (s >= mStencilSize)) {
834 int r = findConfigAttrib(egl, display, config,
835 EGL10.EGL_RED_SIZE, 0);
836 int g = findConfigAttrib(egl, display, config,
837 EGL10.EGL_GREEN_SIZE, 0);
838 int b = findConfigAttrib(egl, display, config,
839 EGL10.EGL_BLUE_SIZE, 0);
840 int a = findConfigAttrib(egl, display, config,
841 EGL10.EGL_ALPHA_SIZE, 0);
842 if ((r == mRedSize) && (g == mGreenSize)
843 && (b == mBlueSize) && (a == mAlphaSize)) {
851 private int findConfigAttrib(EGL10 egl, EGLDisplay display,
852 EGLConfig config, int attribute, int defaultValue) {
854 if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
860 private int[] mValue;
861 // Subclasses can adjust these values:
862 protected int mRedSize;
863 protected int mGreenSize;
864 protected int mBlueSize;
865 protected int mAlphaSize;
866 protected int mDepthSize;
867 protected int mStencilSize;
871 * This class will choose a RGB_565 surface with
872 * or without a depth buffer.
875 private class SimpleEGLConfigChooser extends ComponentSizeChooser {
876 public SimpleEGLConfigChooser(boolean withDepthBuffer) {
877 super(5, 6, 5, 0, withDepthBuffer ? 16 : 0, 0);
882 * An EGL helper class.
885 private class EglHelper {
891 * Initialize EGL for a given configuration spec.
894 public void start() {
896 Log.w("EglHelper", "start() tid=" + Thread.currentThread().getId());
899 * Get an EGL instance
901 mEgl = (EGL10) EGLContext.getEGL();
904 * Get to the default display.
906 mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
908 if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
909 throw new RuntimeException("eglGetDisplay failed");
913 * We can now initialize EGL for that display
915 int[] version = new int[2];
916 if(!mEgl.eglInitialize(mEglDisplay, version)) {
917 throw new RuntimeException("eglInitialize failed");
919 mEglConfig = mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
922 * Create an EGL context. We want to do this as rarely as we can, because an
923 * EGL context is a somewhat heavy object.
925 mEglContext = mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
926 if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
928 throwEglException("createContext");
931 Log.w("EglHelper", "createContext " + mEglContext + " tid=" + Thread.currentThread().getId());
938 * React to the creation of a new surface by creating and returning an
939 * OpenGL interface that renders to that surface.
941 public GL createSurface(SurfaceHolder holder) {
943 Log.w("EglHelper", "createSurface() tid=" + Thread.currentThread().getId());
946 * Check preconditions.
949 throw new RuntimeException("egl not initialized");
951 if (mEglDisplay == null) {
952 throw new RuntimeException("eglDisplay not initialized");
954 if (mEglConfig == null) {
955 throw new RuntimeException("mEglConfig not initialized");
958 * The window size has changed, so we need to create a new
961 if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
964 * Unbind and destroy the old EGL surface, if
967 mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
968 EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
969 mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
973 * Create an EGL surface we can render into.
975 mEglSurface = mEGLWindowSurfaceFactory.createWindowSurface(mEgl,
976 mEglDisplay, mEglConfig, holder);
978 if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
979 int error = mEgl.eglGetError();
980 if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
981 Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
984 throwEglException("createWindowSurface", error);
988 * Before we can issue GL commands, we need to make sure
989 * the context is current and bound to a surface.
991 if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
992 throwEglException("eglMakeCurrent");
995 GL gl = mEglContext.getGL();
996 if (mGLWrapper != null) {
997 gl = mGLWrapper.wrap(gl);
1000 if ((mDebugFlags & (DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS)) != 0) {
1001 int configFlags = 0;
1003 if ((mDebugFlags & DEBUG_CHECK_GL_ERROR) != 0) {
1004 configFlags |= GLDebugHelper.CONFIG_CHECK_GL_ERROR;
1006 if ((mDebugFlags & DEBUG_LOG_GL_CALLS) != 0) {
1007 log = new LogWriter();
1009 gl = GLDebugHelper.wrap(gl, configFlags, log);
1015 * Display the current render surface.
1016 * @return false if the context has been lost.
1018 public boolean swap() {
1019 if (! mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
1022 * 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.
1027 int error = mEgl.eglGetError();
1029 case EGL11.EGL_CONTEXT_LOST:
1031 case EGL10.EGL_BAD_NATIVE_WINDOW:
1032 // The native window is bad, probably because the
1033 // window manager has closed it. Ignore this error,
1034 // on the expectation that the application will be closed soon.
1035 Log.e("EglHelper", "eglSwapBuffers returned EGL_BAD_NATIVE_WINDOW. tid=" + Thread.currentThread().getId());
1038 throwEglException("eglSwapBuffers", error);
1044 public void destroySurface() {
1046 Log.w("EglHelper", "destroySurface() tid=" + Thread.currentThread().getId());
1048 if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
1049 mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
1050 EGL10.EGL_NO_SURFACE,
1051 EGL10.EGL_NO_CONTEXT);
1052 mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
1057 public void finish() {
1059 Log.w("EglHelper", "finish() tid=" + Thread.currentThread().getId());
1061 if (mEglContext != null) {
1062 mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext);
1065 if (mEglDisplay != null) {
1066 mEgl.eglTerminate(mEglDisplay);
1071 private void throwEglException(String function) {
1072 throwEglException(function, mEgl.eglGetError());
1075 private void throwEglException(String function, int error) {
1076 String message = function + " failed: " + EGLLogWrapper.getErrorString(error);
1078 Log.e("EglHelper", "throwEglException tid=" + Thread.currentThread().getId() + " " + message);
1080 throw new RuntimeException(message);
1084 EGLDisplay mEglDisplay;
1085 EGLSurface mEglSurface;
1086 EGLConfig mEglConfig;
1087 EGLContext mEglContext;
1092 * A generic GL Thread. Takes care of initializing EGL and GL. Delegates
1093 * to a Renderer instance to do the actual drawing. Can be configured to
1094 * render continuously or on request.
1096 * All potentially blocking synchronization is done through the
1097 * sGLThreadManager object. This avoids multiple-lock ordering issues.
1100 class GLThread extends Thread {
1101 GLThread(Renderer renderer) {
1105 mRequestRender = true;
1106 mRenderMode = RENDERMODE_CONTINUOUSLY;
1107 mRenderer = renderer;
1112 setName("GLThread " + getId());
1114 Log.i("GLThread", "starting tid=" + getId());
1119 } catch (InterruptedException e) {
1120 // fall thru and exit normally
1122 sGLThreadManager.threadExiting(this);
1127 * This private method should only be called inside a
1128 * synchronized(sGLThreadManager) block.
1130 private void stopEglSurfaceLocked() {
1131 if (mHaveEglSurface) {
1132 mHaveEglSurface = false;
1133 mEglHelper.destroySurface();
1138 * This private method should only be called inside a
1139 * synchronized(sGLThreadManager) block.
1141 private void stopEglContextLocked() {
1142 if (mHaveEglContext) {
1143 mEglHelper.finish();
1144 mHaveEglContext = false;
1145 sGLThreadManager.releaseEglContextLocked(this);
1148 private void guardedRun() throws InterruptedException {
1149 mEglHelper = new EglHelper();
1150 mHaveEglContext = false;
1151 mHaveEglSurface = false;
1154 boolean createEglContext = false;
1155 boolean createEglSurface = false;
1156 boolean lostEglContext = false;
1157 boolean sizeChanged = false;
1158 boolean wantRenderNotification = false;
1159 boolean doRenderNotification = false;
1160 boolean askedToReleaseEglContext = false;
1163 Runnable event = null;
1166 synchronized (sGLThreadManager) {
1172 if (! mEventQueue.isEmpty()) {
1173 event = mEventQueue.remove(0);
1177 // Update the pause state.
1178 if (mPaused != mRequestPaused) {
1179 mPaused = mRequestPaused;
1180 sGLThreadManager.notifyAll();
1181 if (LOG_PAUSE_RESUME) {
1182 Log.i("GLThread", "mPaused is now " + mPaused + " tid=" + getId());
1186 // Do we need to give up the EGL context?
1187 if (mShouldReleaseEglContext) {
1189 Log.i("GLThread", "releasing EGL context because asked to tid=" + getId());
1191 stopEglSurfaceLocked();
1192 stopEglContextLocked();
1193 mShouldReleaseEglContext = false;
1194 askedToReleaseEglContext = true;
1197 // Have we lost the EGL context?
1198 if (lostEglContext) {
1199 stopEglSurfaceLocked();
1200 stopEglContextLocked();
1201 lostEglContext = false;
1204 // Do we need to release the EGL surface?
1205 if (mHaveEglSurface && mPaused) {
1207 Log.i("GLThread", "releasing EGL surface because paused tid=" + getId());
1209 stopEglSurfaceLocked();
1210 if (sGLThreadManager.shouldReleaseEGLContextWhenPausing()) {
1211 stopEglContextLocked();
1213 Log.i("GLThread", "releasing EGL context because paused tid=" + getId());
1216 if (sGLThreadManager.shouldTerminateEGLWhenPausing()) {
1217 mEglHelper.finish();
1219 Log.i("GLThread", "terminating EGL because paused tid=" + getId());
1224 // Have we lost the surface view surface?
1225 if ((! mHasSurface) && (! mWaitingForSurface)) {
1227 Log.i("GLThread", "noticed surfaceView surface lost tid=" + getId());
1229 if (mHaveEglSurface) {
1230 stopEglSurfaceLocked();
1232 mWaitingForSurface = true;
1233 sGLThreadManager.notifyAll();
1236 // Have we acquired the surface view surface?
1237 if (mHasSurface && mWaitingForSurface) {
1239 Log.i("GLThread", "noticed surfaceView surface acquired tid=" + getId());
1241 mWaitingForSurface = false;
1242 sGLThreadManager.notifyAll();
1245 if (doRenderNotification) {
1247 Log.i("GLThread", "sending render notification tid=" + getId());
1249 wantRenderNotification = false;
1250 doRenderNotification = false;
1251 mRenderComplete = true;
1252 sGLThreadManager.notifyAll();
1256 if (readyToDraw()) {
1258 // If we don't have an EGL context, try to acquire one.
1259 if (! mHaveEglContext) {
1260 if (askedToReleaseEglContext) {
1261 askedToReleaseEglContext = false;
1262 } else if (sGLThreadManager.tryAcquireEglContextLocked(this)) {
1265 } catch (RuntimeException t) {
1266 sGLThreadManager.releaseEglContextLocked(this);
1269 mHaveEglContext = true;
1270 createEglContext = true;
1272 sGLThreadManager.notifyAll();
1276 if (mHaveEglContext && !mHaveEglSurface) {
1277 mHaveEglSurface = true;
1278 createEglSurface = true;
1282 if (mHaveEglSurface) {
1287 wantRenderNotification = true;
1289 Log.i("GLThread", "noticing that we want render notification tid=" + getId());
1292 if (DRAW_TWICE_AFTER_SIZE_CHANGED) {
1293 // We keep mRequestRender true so that we draw twice after the size changes.
1294 // (Once because of mSizeChanged, the second time because of mRequestRender.)
1295 // This forces the updated graphics onto the screen.
1297 mRequestRender = false;
1299 mSizeChanged = false;
1301 mRequestRender = false;
1303 sGLThreadManager.notifyAll();
1308 // By design, this is the only place in a GLThread thread where we wait().
1310 Log.i("GLThread", "waiting tid=" + getId()
1311 + " mHaveEglContext: " + mHaveEglContext
1312 + " mHaveEglSurface: " + mHaveEglSurface
1313 + " mPaused: " + mPaused
1314 + " mHasSurface: " + mHasSurface
1315 + " mWaitingForSurface: " + mWaitingForSurface
1316 + " mWidth: " + mWidth
1317 + " mHeight: " + mHeight
1318 + " mRequestRender: " + mRequestRender
1319 + " mRenderMode: " + mRenderMode);
1321 sGLThreadManager.wait();
1323 } // end of synchronized(sGLThreadManager)
1325 if (event != null) {
1331 if (createEglSurface) {
1333 Log.w("GLThread", "egl createSurface");
1335 gl = (GL10) mEglHelper.createSurface(getHolder());
1337 // Couldn't create a surface. Quit quietly.
1340 sGLThreadManager.checkGLDriver(gl);
1341 createEglSurface = false;
1344 if (createEglContext) {
1346 Log.w("GLThread", "onSurfaceCreated");
1348 mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
1349 createEglContext = false;
1354 Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");
1356 mRenderer.onSurfaceChanged(gl, w, h);
1357 sizeChanged = false;
1360 if (LOG_RENDERER_DRAW_FRAME) {
1361 Log.w("GLThread", "onDrawFrame tid=" + getId());
1363 mRenderer.onDrawFrame(gl);
1364 if (!mEglHelper.swap()) {
1366 Log.i("GLThread", "egl context lost tid=" + getId());
1368 lostEglContext = true;
1371 if (wantRenderNotification) {
1372 doRenderNotification = true;
1378 * clean-up everything...
1380 synchronized (sGLThreadManager) {
1381 stopEglSurfaceLocked();
1382 stopEglContextLocked();
1387 public boolean ableToDraw() {
1388 return mHaveEglContext && mHaveEglSurface && readyToDraw();
1391 private boolean readyToDraw() {
1392 return (!mPaused) && mHasSurface
1393 && (mWidth > 0) && (mHeight > 0)
1394 && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY));
1397 public void setRenderMode(int renderMode) {
1398 if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) {
1399 throw new IllegalArgumentException("renderMode");
1401 synchronized(sGLThreadManager) {
1402 mRenderMode = renderMode;
1403 sGLThreadManager.notifyAll();
1407 public int getRenderMode() {
1408 synchronized(sGLThreadManager) {
1413 public void requestRender() {
1414 synchronized(sGLThreadManager) {
1415 mRequestRender = true;
1416 sGLThreadManager.notifyAll();
1420 public void surfaceCreated() {
1421 synchronized(sGLThreadManager) {
1423 Log.i("GLThread", "surfaceCreated tid=" + getId());
1426 sGLThreadManager.notifyAll();
1427 while((mWaitingForSurface) && (!mExited)) {
1429 sGLThreadManager.wait();
1430 } catch (InterruptedException e) {
1431 Thread.currentThread().interrupt();
1437 public void surfaceDestroyed() {
1438 synchronized(sGLThreadManager) {
1440 Log.i("GLThread", "surfaceDestroyed tid=" + getId());
1442 mHasSurface = false;
1443 sGLThreadManager.notifyAll();
1444 while((!mWaitingForSurface) && (!mExited)) {
1446 sGLThreadManager.wait();
1447 } catch (InterruptedException e) {
1448 Thread.currentThread().interrupt();
1454 public void onPause() {
1455 synchronized (sGLThreadManager) {
1456 if (LOG_PAUSE_RESUME) {
1457 Log.i("GLThread", "onPause tid=" + getId());
1459 mRequestPaused = true;
1460 sGLThreadManager.notifyAll();
1461 while ((! mExited) && (! mPaused)) {
1462 if (LOG_PAUSE_RESUME) {
1463 Log.i("Main thread", "onPause waiting for mPaused.");
1466 sGLThreadManager.wait();
1467 } catch (InterruptedException ex) {
1468 Thread.currentThread().interrupt();
1474 public void onResume() {
1475 synchronized (sGLThreadManager) {
1476 if (LOG_PAUSE_RESUME) {
1477 Log.i("GLThread", "onResume tid=" + getId());
1479 mRequestPaused = false;
1480 mRequestRender = true;
1481 mRenderComplete = false;
1482 sGLThreadManager.notifyAll();
1483 while ((! mExited) && mPaused && (!mRenderComplete)) {
1484 if (LOG_PAUSE_RESUME) {
1485 Log.i("Main thread", "onResume waiting for !mPaused.");
1488 sGLThreadManager.wait();
1489 } catch (InterruptedException ex) {
1490 Thread.currentThread().interrupt();
1496 public void onWindowResize(int w, int h) {
1497 synchronized (sGLThreadManager) {
1500 mSizeChanged = true;
1501 mRequestRender = true;
1502 mRenderComplete = false;
1503 sGLThreadManager.notifyAll();
1505 // Wait for thread to react to resize and render a frame
1506 while (! mExited && !mPaused && !mRenderComplete
1507 && (mGLThread != null && mGLThread.ableToDraw())) {
1509 Log.i("Main thread", "onWindowResize waiting for render complete from tid=" + mGLThread.getId());
1512 sGLThreadManager.wait();
1513 } catch (InterruptedException ex) {
1514 Thread.currentThread().interrupt();
1520 public void requestExitAndWait() {
1521 // don't call this from GLThread thread or it is a guaranteed
1523 synchronized(sGLThreadManager) {
1525 sGLThreadManager.notifyAll();
1528 sGLThreadManager.wait();
1529 } catch (InterruptedException ex) {
1530 Thread.currentThread().interrupt();
1536 public void requestReleaseEglContextLocked() {
1537 mShouldReleaseEglContext = true;
1538 sGLThreadManager.notifyAll();
1542 * Queue an "event" to be run on the GL rendering thread.
1543 * @param r the runnable to be run on the GL rendering thread.
1545 public void queueEvent(Runnable r) {
1547 throw new IllegalArgumentException("r must not be null");
1549 synchronized(sGLThreadManager) {
1551 sGLThreadManager.notifyAll();
1555 // Once the thread is started, all accesses to the following member
1556 // variables are protected by the sGLThreadManager monitor
1557 private boolean mShouldExit;
1558 private boolean mExited;
1559 private boolean mRequestPaused;
1560 private boolean mPaused;
1561 private boolean mHasSurface;
1562 private boolean mWaitingForSurface;
1563 private boolean mHaveEglContext;
1564 private boolean mHaveEglSurface;
1565 private boolean mShouldReleaseEglContext;
1567 private int mHeight;
1568 private int mRenderMode;
1569 private boolean mRequestRender;
1570 private boolean mRenderComplete;
1571 private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>();
1573 // End of member variables protected by the sGLThreadManager monitor.
1575 private Renderer mRenderer;
1576 private EglHelper mEglHelper;
1579 static class LogWriter extends Writer {
1581 @Override public void close() {
1585 @Override public void flush() {
1589 @Override public void write(char[] buf, int offset, int count) {
1590 for(int i = 0; i < count; i++) {
1591 char c = buf[offset + i];
1601 private void flushBuilder() {
1602 if (mBuilder.length() > 0) {
1603 Log.v("GLSurfaceView", mBuilder.toString());
1604 mBuilder.delete(0, mBuilder.length());
1608 private StringBuilder mBuilder = new StringBuilder();
1612 private void checkRenderThreadState() {
1613 if (mGLThread != null) {
1614 throw new IllegalStateException(
1615 "setRenderer has already been called for this instance.");
1619 private static class GLThreadManager {
1620 private static String TAG = "GLThreadManager";
1622 public synchronized void threadExiting(GLThread thread) {
1624 Log.i("GLThread", "exiting tid=" + thread.getId());
1626 thread.mExited = true;
1627 if (mEglOwner == thread) {
1634 * Tries once to acquire the right to use an EGL
1635 * context. Does not block. Requires that we are already
1636 * in the sGLThreadManager monitor when this is called.
1638 * @return true if the right to use an EGL context was acquired.
1640 public boolean tryAcquireEglContextLocked(GLThread thread) {
1641 if (mEglOwner == thread || mEglOwner == null) {
1647 if (mMultipleGLESContextsAllowed) {
1650 // Notify the owning thread that it should release the context.
1651 // TODO: implement a fairness policy. Currently
1652 // if the owning thread is drawing continuously it will just
1653 // reacquire the EGL context.
1654 if (mEglOwner != null) {
1655 mEglOwner.requestReleaseEglContextLocked();
1661 * Releases the EGL context. Requires that we are already in the
1662 * sGLThreadManager monitor when this is called.
1664 public void releaseEglContextLocked(GLThread thread) {
1665 if (mEglOwner == thread) {
1671 public synchronized boolean shouldReleaseEGLContextWhenPausing() {
1672 // Release the EGL context when pausing even if
1673 // the hardware supports multiple EGL contexts.
1674 // Otherwise the device could run out of EGL contexts.
1678 public synchronized boolean shouldTerminateEGLWhenPausing() {
1680 return !mMultipleGLESContextsAllowed;
1683 public synchronized void checkGLDriver(GL10 gl) {
1684 if (! mGLESDriverCheckComplete) {
1686 if (mGLESVersion < kGLES_20) {
1687 String renderer = gl.glGetString(GL10.GL_RENDERER);
1688 mMultipleGLESContextsAllowed =
1689 ! renderer.startsWith(kMSM7K_RENDERER_PREFIX);
1691 Log.w(TAG, "checkGLDriver renderer = \"" + renderer + "\" multipleContextsAllowed = "
1692 + mMultipleGLESContextsAllowed);
1696 mGLESDriverCheckComplete = true;
1700 private void checkGLESVersion() {
1701 if (! mGLESVersionCheckComplete) {
1702 mGLESVersion = SystemProperties.getInt(
1703 "ro.opengles.version",
1704 ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
1705 if (mGLESVersion >= kGLES_20) {
1706 mMultipleGLESContextsAllowed = true;
1709 Log.w(TAG, "checkGLESVersion mGLESVersion =" +
1710 " " + mGLESVersion + " mMultipleGLESContextsAllowed = " + mMultipleGLESContextsAllowed);
1712 mGLESVersionCheckComplete = true;
1716 private boolean mGLESVersionCheckComplete;
1717 private int mGLESVersion;
1718 private boolean mGLESDriverCheckComplete;
1719 private boolean mMultipleGLESContextsAllowed;
1720 private static final int kGLES_20 = 0x20000;
1721 private static final String kMSM7K_RENDERER_PREFIX =
1722 "Q3Dimension MSM7500 ";
1723 private GLThread mEglOwner;
1726 private static final GLThreadManager sGLThreadManager = new GLThreadManager();
1727 private boolean mSizeChanged = true;
1729 private GLThread mGLThread;
1730 private EGLConfigChooser mEGLConfigChooser;
1731 private EGLContextFactory mEGLContextFactory;
1732 private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;
1733 private GLWrapper mGLWrapper;
1734 private int mDebugFlags;
1735 private int mEGLContextClientVersion;