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