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