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