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