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