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