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