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