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