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