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