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