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