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