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