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