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