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