GLSurfaceView.java revision aa396b9610f339cf280159144fbea47506f060e7
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 int w = 0; 1070 int h = 0; 1071 Runnable event = null; 1072 1073 while (true) { 1074 synchronized (sGLThreadManager) { 1075 while (true) { 1076 if (mShouldExit) { 1077 return; 1078 } 1079 1080 if (! mEventQueue.isEmpty()) { 1081 event = mEventQueue.remove(0); 1082 break; 1083 } 1084 1085 // Do we need to release the EGL surface? 1086 if (mHaveEgl && mPaused) { 1087 if (LOG_SURFACE) { 1088 Log.i("GLThread", "releasing EGL surface because paused tid=" + getId()); 1089 } 1090 stopEglLocked(); 1091 } 1092 1093 // Have we lost the surface view surface? 1094 if ((! mHasSurface) && (! mWaitingForSurface)) { 1095 if (LOG_SURFACE) { 1096 Log.i("GLThread", "noticed surfaceView surface lost tid=" + getId()); 1097 } 1098 if (mHaveEgl) { 1099 stopEglLocked(); 1100 } 1101 mWaitingForSurface = true; 1102 sGLThreadManager.notifyAll(); 1103 } 1104 1105 // Have we acquired the surface view surface? 1106 if (mHasSurface && mWaitingForSurface) { 1107 if (LOG_SURFACE) { 1108 Log.i("GLThread", "noticed surfaceView surface acquired tid=" + getId()); 1109 } 1110 mWaitingForSurface = false; 1111 sGLThreadManager.notifyAll(); 1112 } 1113 1114 // Ready to draw? 1115 if ((!mPaused) && mHasSurface 1116 && (mWidth > 0) && (mHeight > 0) 1117 && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY))) { 1118 1119 // If we don't have an egl surface, try to acquire one. 1120 if ((! mHaveEgl) && sGLThreadManager.tryAcquireEglSurfaceLocked(this)) { 1121 mHaveEgl = true; 1122 mEglHelper.start(); 1123 createEglSurface = true; 1124 sizeChanged = true; 1125 sGLThreadManager.notifyAll(); 1126 } 1127 1128 if (mHaveEgl) { 1129 if (mSizeChanged) { 1130 sizeChanged = true; 1131 w = mWidth; 1132 h = mHeight; 1133 if (DRAW_TWICE_AFTER_SIZE_CHANGED) { 1134 // We keep mRequestRender true so that we draw twice after the size changes. 1135 // (Once because of mSizeChanged, the second time because of mRequestRender.) 1136 // This forces the updated graphics onto the screen. 1137 } else { 1138 mRequestRender = false; 1139 } 1140 mSizeChanged = false; 1141 } else { 1142 mRequestRender = false; 1143 } 1144 sGLThreadManager.notifyAll(); 1145 break; 1146 } 1147 } 1148 1149 // By design, this is the only place in a GLThread thread where we wait(). 1150 if (LOG_THREADS) { 1151 Log.i("GLThread", "waiting tid=" + getId()); 1152 } 1153 sGLThreadManager.wait(); 1154 } 1155 } // end of synchronized(sGLThreadManager) 1156 1157 if (event != null) { 1158 event.run(); 1159 event = null; 1160 continue; 1161 } 1162 1163 if (createEglSurface) { 1164 gl = (GL10) mEglHelper.createSurface(getHolder()); 1165 sGLThreadManager.checkGLDriver(gl); 1166 if (LOG_RENDERER) { 1167 Log.w("GLThread", "onSurfaceCreated"); 1168 } 1169 mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig); 1170 createEglSurface = false; 1171 } 1172 1173 if (sizeChanged) { 1174 if (LOG_RENDERER) { 1175 Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")"); 1176 } 1177 mRenderer.onSurfaceChanged(gl, w, h); 1178 sizeChanged = false; 1179 } 1180 1181 if (LOG_RENDERER) { 1182 Log.w("GLThread", "onDrawFrame"); 1183 } 1184 mRenderer.onDrawFrame(gl); 1185 if(!mEglHelper.swap()) { 1186 if (LOG_SURFACE) { 1187 Log.i("GLThread", "egl surface lost tid=" + getId()); 1188 } 1189 } 1190 } 1191 } finally { 1192 /* 1193 * clean-up everything... 1194 */ 1195 synchronized (sGLThreadManager) { 1196 stopEglLocked(); 1197 } 1198 } 1199 } 1200 1201 public void setRenderMode(int renderMode) { 1202 if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) { 1203 throw new IllegalArgumentException("renderMode"); 1204 } 1205 synchronized(sGLThreadManager) { 1206 mRenderMode = renderMode; 1207 sGLThreadManager.notifyAll(); 1208 } 1209 } 1210 1211 public int getRenderMode() { 1212 synchronized(sGLThreadManager) { 1213 return mRenderMode; 1214 } 1215 } 1216 1217 public void requestRender() { 1218 synchronized(sGLThreadManager) { 1219 mRequestRender = true; 1220 sGLThreadManager.notifyAll(); 1221 } 1222 } 1223 1224 public void surfaceCreated() { 1225 synchronized(sGLThreadManager) { 1226 if (LOG_THREADS) { 1227 Log.i("GLThread", "surfaceCreated tid=" + getId()); 1228 } 1229 mHasSurface = true; 1230 sGLThreadManager.notifyAll(); 1231 } 1232 } 1233 1234 public void surfaceDestroyed() { 1235 synchronized(sGLThreadManager) { 1236 if (LOG_THREADS) { 1237 Log.i("GLThread", "surfaceDestroyed tid=" + getId()); 1238 } 1239 mHasSurface = false; 1240 sGLThreadManager.notifyAll(); 1241 while((!mWaitingForSurface) && (!mExited)) { 1242 try { 1243 sGLThreadManager.wait(); 1244 } catch (InterruptedException e) { 1245 Thread.currentThread().interrupt(); 1246 } 1247 } 1248 } 1249 } 1250 1251 public void onPause() { 1252 synchronized (sGLThreadManager) { 1253 mPaused = true; 1254 sGLThreadManager.notifyAll(); 1255 } 1256 } 1257 1258 public void onResume() { 1259 synchronized (sGLThreadManager) { 1260 mPaused = false; 1261 mRequestRender = true; 1262 sGLThreadManager.notifyAll(); 1263 } 1264 } 1265 1266 public void onWindowResize(int w, int h) { 1267 synchronized (sGLThreadManager) { 1268 mWidth = w; 1269 mHeight = h; 1270 mSizeChanged = true; 1271 mRequestRender = true; 1272 sGLThreadManager.notifyAll(); 1273 } 1274 } 1275 1276 public void requestExitAndWait() { 1277 // don't call this from GLThread thread or it is a guaranteed 1278 // deadlock! 1279 synchronized(sGLThreadManager) { 1280 mShouldExit = true; 1281 sGLThreadManager.notifyAll(); 1282 while (! mExited) { 1283 try { 1284 sGLThreadManager.wait(); 1285 } catch (InterruptedException ex) { 1286 Thread.currentThread().interrupt(); 1287 } 1288 } 1289 } 1290 } 1291 1292 /** 1293 * Queue an "event" to be run on the GL rendering thread. 1294 * @param r the runnable to be run on the GL rendering thread. 1295 */ 1296 public void queueEvent(Runnable r) { 1297 if (r == null) { 1298 throw new IllegalArgumentException("r must not be null"); 1299 } 1300 synchronized(sGLThreadManager) { 1301 mEventQueue.add(r); 1302 sGLThreadManager.notifyAll(); 1303 } 1304 } 1305 1306 // Once the thread is started, all accesses to the following member 1307 // variables are protected by the sGLThreadManager monitor 1308 private boolean mShouldExit; 1309 private boolean mExited; 1310 private boolean mPaused; 1311 private boolean mHasSurface; 1312 private boolean mWaitingForSurface; 1313 private boolean mHaveEgl; 1314 private int mWidth; 1315 private int mHeight; 1316 private int mRenderMode; 1317 private boolean mRequestRender; 1318 private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>(); 1319 // End of member variables protected by the sGLThreadManager monitor. 1320 1321 private Renderer mRenderer; 1322 private EglHelper mEglHelper; 1323 } 1324 1325 static class LogWriter extends Writer { 1326 1327 @Override public void close() { 1328 flushBuilder(); 1329 } 1330 1331 @Override public void flush() { 1332 flushBuilder(); 1333 } 1334 1335 @Override public void write(char[] buf, int offset, int count) { 1336 for(int i = 0; i < count; i++) { 1337 char c = buf[offset + i]; 1338 if ( c == '\n') { 1339 flushBuilder(); 1340 } 1341 else { 1342 mBuilder.append(c); 1343 } 1344 } 1345 } 1346 1347 private void flushBuilder() { 1348 if (mBuilder.length() > 0) { 1349 Log.v("GLSurfaceView", mBuilder.toString()); 1350 mBuilder.delete(0, mBuilder.length()); 1351 } 1352 } 1353 1354 private StringBuilder mBuilder = new StringBuilder(); 1355 } 1356 1357 1358 private void checkRenderThreadState() { 1359 if (mGLThread != null) { 1360 throw new IllegalStateException( 1361 "setRenderer has already been called for this instance."); 1362 } 1363 } 1364 1365 private static class GLThreadManager { 1366 1367 public synchronized void threadExiting(GLThread thread) { 1368 if (LOG_THREADS) { 1369 Log.i("GLThread", "exiting tid=" + thread.getId()); 1370 } 1371 thread.mExited = true; 1372 if (mEglOwner == thread) { 1373 mEglOwner = null; 1374 } 1375 notifyAll(); 1376 } 1377 1378 /* 1379<<<<<<< HEAD 1380 * Tries to acquire the right to use an EGL 1381 * surface. Does not block. 1382======= 1383 * Tries once to acquire the right to use an EGL 1384 * surface. Does not block. Requires that we are already 1385 * in the sGLThreadManager monitor when this is called. 1386>>>>>>> dc49acb0 1387 * @return true if the right to use an EGL surface was acquired. 1388 */ 1389 public boolean tryAcquireEglSurfaceLocked(GLThread thread) { 1390 if (mEglOwner == thread || mEglOwner == null) { 1391 mEglOwner = thread; 1392 notifyAll(); 1393 return true; 1394 } 1395 checkGLESVersion(); 1396 if (mMultipleGLESContextsAllowed) { 1397 return true; 1398 } 1399 return false; 1400 } 1401 /* 1402 * Releases the EGL surface. Requires that we are already in the 1403 * sGLThreadManager monitor when this is called. 1404 */ 1405 public void releaseEglSurfaceLocked(GLThread thread) { 1406 if (mEglOwner == thread) { 1407 mEglOwner = null; 1408 } 1409 notifyAll(); 1410 } 1411 1412 public synchronized void checkGLDriver(GL10 gl) { 1413 if (! mGLESDriverCheckComplete) { 1414 checkGLESVersion(); 1415 if (mGLESVersion < kGLES_20) { 1416 String renderer = gl.glGetString(GL10.GL_RENDERER); 1417 mMultipleGLESContextsAllowed = 1418 ! renderer.startsWith(kMSM7K_RENDERER_PREFIX); 1419 notifyAll(); 1420 } 1421 mGLESDriverCheckComplete = true; 1422 } 1423 } 1424 1425 private void checkGLESVersion() { 1426 if (! mGLESVersionCheckComplete) { 1427 mGLESVersion = SystemProperties.getInt( 1428 "ro.opengles.version", 1429 ConfigurationInfo.GL_ES_VERSION_UNDEFINED); 1430 if (mGLESVersion >= kGLES_20) { 1431 mMultipleGLESContextsAllowed = true; 1432 } 1433 mGLESVersionCheckComplete = true; 1434 } 1435 1436 } 1437 1438 private boolean mGLESVersionCheckComplete; 1439 private int mGLESVersion; 1440 private boolean mGLESDriverCheckComplete; 1441 private boolean mMultipleGLESContextsAllowed; 1442 private int mGLContextCount; 1443 private static final int kGLES_20 = 0x20000; 1444 private static final String kMSM7K_RENDERER_PREFIX = 1445 "Q3Dimension MSM7500 "; 1446 private GLThread mEglOwner; 1447 } 1448 1449 private static final GLThreadManager sGLThreadManager = new GLThreadManager(); 1450 private boolean mSizeChanged = true; 1451 1452 private GLThread mGLThread; 1453 private EGLConfigChooser mEGLConfigChooser; 1454 private EGLContextFactory mEGLContextFactory; 1455 private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory; 1456 private GLWrapper mGLWrapper; 1457 private int mDebugFlags; 1458 private int mEGLContextClientVersion; 1459} 1460