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 17 package com.badlogic.gdx.backends.android.surfaceview; 18 19 import java.io.Writer; 20 import java.lang.ref.WeakReference; 21 import java.util.ArrayList; 22 23 import javax.microedition.khronos.egl.EGL10; 24 import javax.microedition.khronos.egl.EGL11; 25 import javax.microedition.khronos.egl.EGLConfig; 26 import javax.microedition.khronos.egl.EGLContext; 27 import javax.microedition.khronos.egl.EGLDisplay; 28 import javax.microedition.khronos.egl.EGLSurface; 29 import javax.microedition.khronos.opengles.GL; 30 import javax.microedition.khronos.opengles.GL10; 31 32 import android.content.Context; 33 //import android.content.pm.ConfigurationInfo; 34 import android.graphics.PixelFormat; 35 import android.opengl.GLDebugHelper; 36 import android.opengl.GLSurfaceView.EGLConfigChooser; 37 import android.opengl.GLSurfaceView.Renderer; 38 //import android.os.SystemProperties; 39 import android.util.AttributeSet; 40 import android.util.Log; 41 import android.view.SurfaceHolder; 42 import android.view.SurfaceView; 43 44 /** 45 * 46 * <b>This class is a slightly modified copy of android.opengl.GLSurfaceView 47 * from Android 4.3 source code (API 18). 48 * It is intended to be used on Android 2.x if you need proper support for 49 * onAttachedToWindow and onDetachedFromWindow methods.<b> 50 * <p> 51 * 52 * It's an implementation of SurfaceView that uses the dedicated surface for 53 * displaying OpenGL rendering. 54 * <p> 55 * A GLSurfaceView provides the following features: 56 * <p> 57 * <ul> 58 * <li>Manages a surface, which is a special piece of memory that can be 59 * composited into the Android view system. 60 * <li>Manages an EGL display, which enables OpenGL to render into a surface. 61 * <li>Accepts a user-provided Renderer object that does the actual rendering. 62 * <li>Renders on a dedicated thread to decouple rendering performance from the 63 * UI thread. 64 * <li>Supports both on-demand and continuous rendering. 65 * <li>Optionally wraps, traces, and/or error-checks the renderer's OpenGL calls. 66 * </ul> 67 * 68 * <div class="special reference"> 69 * <h3>Developer Guides</h3> 70 * <p>For more information about how to use OpenGL, read the 71 * <a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a> developer guide.</p> 72 * </div> 73 * 74 * <h3>Using GLSurfaceView</h3> 75 * <p> 76 * Typically you use GLSurfaceView by subclassing it and overriding one or more of the 77 * View system input event methods. If your application does not need to override event 78 * methods then GLSurfaceView can be used as-is. For the most part 79 * GLSurfaceView behavior is customized by calling "set" methods rather than by subclassing. 80 * For example, unlike a regular View, drawing is delegated to a separate Renderer object which 81 * is registered with the GLSurfaceView 82 * using the {@link #setRenderer(Renderer)} call. 83 * <p> 84 * <h3>Initializing GLSurfaceView</h3> 85 * All you have to do to initialize a GLSurfaceView is call {@link #setRenderer(Renderer)}. 86 * However, if desired, you can modify the default behavior of GLSurfaceView by calling one or 87 * more of these methods before calling setRenderer: 88 * <ul> 89 * <li>{@link #setDebugFlags(int)} 90 * <li>{@link #setEGLConfigChooser(boolean)} 91 * <li>{@link #setEGLConfigChooser(EGLConfigChooser)} 92 * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)} 93 * <li>{@link #setGLWrapper(GLWrapper)} 94 * </ul> 95 * <p> 96 * <h4>Specifying the android.view.Surface</h4> 97 * By default GLSurfaceView will create a PixelFormat.RGB_888 format surface. If a translucent 98 * surface is required, call getHolder().setFormat(PixelFormat.TRANSLUCENT). 99 * The exact format of a TRANSLUCENT surface is device dependent, but it will be 100 * a 32-bit-per-pixel surface with 8 bits per component. 101 * <p> 102 * <h4>Choosing an EGL Configuration</h4> 103 * A given Android device may support multiple EGLConfig rendering configurations. 104 * The available configurations may differ in how may channels of data are present, as 105 * well as how many bits are allocated to each channel. Therefore, the first thing 106 * GLSurfaceView has to do when starting to render is choose what EGLConfig to use. 107 * <p> 108 * By default GLSurfaceView chooses a EGLConfig that has an RGB_888 pixel format, 109 * with at least a 16-bit depth buffer and no stencil. 110 * <p> 111 * If you would prefer a different EGLConfig 112 * you can override the default behavior by calling one of the 113 * setEGLConfigChooser methods. 114 * <p> 115 * <h4>Debug Behavior</h4> 116 * You can optionally modify the behavior of GLSurfaceView by calling 117 * one or more of the debugging methods {@link #setDebugFlags(int)}, 118 * and {@link #setGLWrapper}. These methods may be called before and/or after setRenderer, but 119 * typically they are called before setRenderer so that they take effect immediately. 120 * <p> 121 * <h4>Setting a Renderer</h4> 122 * Finally, you must call {@link #setRenderer} to register a {@link Renderer}. 123 * The renderer is 124 * responsible for doing the actual OpenGL rendering. 125 * <p> 126 * <h3>Rendering Mode</h3> 127 * Once the renderer is set, you can control whether the renderer draws 128 * continuously or on-demand by calling 129 * {@link #setRenderMode}. The default is continuous rendering. 130 * <p> 131 * <h3>Activity Life-cycle</h3> 132 * A GLSurfaceView must be notified when the activity is paused and resumed. GLSurfaceView clients 133 * are required to call {@link #onPause()} when the activity pauses and 134 * {@link #onResume()} when the activity resumes. These calls allow GLSurfaceView to 135 * pause and resume the rendering thread, and also allow GLSurfaceView to release and recreate 136 * the OpenGL display. 137 * <p> 138 * <h3>Handling events</h3> 139 * <p> 140 * To handle an event you will typically subclass GLSurfaceView and override the 141 * appropriate method, just as you would with any other View. However, when handling 142 * the event, you may need to communicate with the Renderer object 143 * that's running in the rendering thread. You can do this using any 144 * standard Java cross-thread communication mechanism. In addition, 145 * one relatively easy way to communicate with your renderer is 146 * to call 147 * {@link #queueEvent(Runnable)}. For example: 148 * <pre class="prettyprint"> 149 * class MyGLSurfaceView extends GLSurfaceView { 150 * 151 * private MyRenderer mMyRenderer; 152 * 153 * public void start() { 154 * mMyRenderer = ...; 155 * setRenderer(mMyRenderer); 156 * } 157 * 158 * public boolean onKeyDown(int keyCode, KeyEvent event) { 159 * if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { 160 * queueEvent(new Runnable() { 161 * // This method will be called on the rendering 162 * // thread: 163 * public void run() { 164 * mMyRenderer.handleDpadCenter(); 165 * }}); 166 * return true; 167 * } 168 * return super.onKeyDown(keyCode, event); 169 * } 170 * } 171 * </pre> 172 * 173 */ 174 @SuppressWarnings("synthetic-access") 175 public class GLSurfaceViewAPI18 extends SurfaceView implements SurfaceHolder.Callback { 176 private final static String TAG = "GLSurfaceViewAPI18"; 177 private final static boolean LOG_ATTACH_DETACH = false; 178 private final static boolean LOG_THREADS = false; 179 private final static boolean LOG_PAUSE_RESUME = false; 180 private final static boolean LOG_SURFACE = false; 181 private final static boolean LOG_RENDERER = false; 182 private final static boolean LOG_RENDERER_DRAW_FRAME = false; 183 private final static boolean LOG_EGL = false; 184 /** 185 * The renderer only renders 186 * when the surface is created, or when {@link #requestRender} is called. 187 * 188 * @see #getRenderMode() 189 * @see #setRenderMode(int) 190 * @see #requestRender() 191 */ 192 public final static int RENDERMODE_WHEN_DIRTY = 0; 193 /** 194 * The renderer is called 195 * continuously to re-render the scene. 196 * 197 * @see #getRenderMode() 198 * @see #setRenderMode(int) 199 */ 200 public final static int RENDERMODE_CONTINUOUSLY = 1; 201 202 /** 203 * Check glError() after every GL call and throw an exception if glError indicates 204 * that an error has occurred. This can be used to help track down which OpenGL ES call 205 * is causing an error. 206 * 207 * @see #getDebugFlags 208 * @see #setDebugFlags 209 */ 210 public final static int DEBUG_CHECK_GL_ERROR = 1; 211 212 /** 213 * Log GL calls to the system log at "verbose" level with tag "GLSurfaceView". 214 * 215 * @see #getDebugFlags 216 * @see #setDebugFlags 217 */ 218 public final static int DEBUG_LOG_GL_CALLS = 2; 219 220 /** 221 * Standard View constructor. In order to render something, you 222 * must call {@link #setRenderer} to register a renderer. 223 */ 224 public GLSurfaceViewAPI18(Context context) { 225 super(context); 226 init(); 227 } 228 229 /** 230 * Standard View constructor. In order to render something, you 231 * must call {@link #setRenderer} to register a renderer. 232 */ 233 public GLSurfaceViewAPI18(Context context, AttributeSet attrs) { 234 super(context, attrs); 235 init(); 236 } 237 238 @Override 239 protected void finalize() throws Throwable { 240 try { 241 if (mGLThread != null) { 242 // GLThread may still be running if this view was never 243 // attached to a window. 244 mGLThread.requestExitAndWait(); 245 } 246 } finally { 247 super.finalize(); 248 } 249 } 250 251 private void init() { 252 // Install a SurfaceHolder.Callback so we get notified when the 253 // underlying surface is created and destroyed 254 SurfaceHolder holder = getHolder(); 255 holder.addCallback(this); 256 int sdkVersion = android.os.Build.VERSION.SDK_INT; 257 // setFormat is done by SurfaceView in SDK 2.3 and newer. 258 if (sdkVersion <= 8) { // SDK 2.2 or older 259 holder.setFormat(PixelFormat.RGB_565); 260 } 261 // setType is not needed for SDK 2.0 or newer. Uncomment this 262 // statement if back-porting this code to older SDKs. 263 // holder.setType(SurfaceHolder.SURFACE_TYPE_GPU); 264 } 265 266 /** 267 * Set the glWrapper. If the glWrapper is not null, its 268 * {@link GLWrapper#wrap(GL)} method is called 269 * whenever a surface is created. A GLWrapper can be used to wrap 270 * the GL object that's passed to the renderer. Wrapping a GL 271 * object enables examining and modifying the behavior of the 272 * GL calls made by the renderer. 273 * <p> 274 * Wrapping is typically used for debugging purposes. 275 * <p> 276 * The default value is null. 277 * @param glWrapper the new GLWrapper 278 */ 279 public void setGLWrapper(GLWrapper glWrapper) { 280 mGLWrapper = glWrapper; 281 } 282 283 /** 284 * Set the debug flags to a new value. The value is 285 * constructed by OR-together zero or more 286 * of the DEBUG_CHECK_* constants. The debug flags take effect 287 * whenever a surface is created. The default value is zero. 288 * @param debugFlags the new debug flags 289 * @see #DEBUG_CHECK_GL_ERROR 290 * @see #DEBUG_LOG_GL_CALLS 291 */ 292 public void setDebugFlags(int debugFlags) { 293 mDebugFlags = debugFlags; 294 } 295 296 /** 297 * Get the current value of the debug flags. 298 * @return the current value of the debug flags. 299 */ 300 public int getDebugFlags() { 301 return mDebugFlags; 302 } 303 304 /** 305 * Control whether the EGL context is preserved when the GLSurfaceView is paused and 306 * resumed. 307 * <p> 308 * If set to true, then the EGL context may be preserved when the GLSurfaceView is paused. 309 * Whether the EGL context is actually preserved or not depends upon whether the 310 * Android device that the program is running on can support an arbitrary number of EGL 311 * contexts or not. Devices that can only support a limited number of EGL contexts must 312 * release the EGL context in order to allow multiple applications to share the GPU. 313 * <p> 314 * If set to false, the EGL context will be released when the GLSurfaceView is paused, 315 * and recreated when the GLSurfaceView is resumed. 316 * <p> 317 * 318 * The default is false. 319 * 320 * @param preserveOnPause preserve the EGL context when paused 321 */ 322 public void setPreserveEGLContextOnPause(boolean preserveOnPause) { 323 mPreserveEGLContextOnPause = preserveOnPause; 324 } 325 326 /** 327 * @return true if the EGL context will be preserved when paused 328 */ 329 public boolean getPreserveEGLContextOnPause() { 330 return mPreserveEGLContextOnPause; 331 } 332 333 /** 334 * Set the renderer associated with this view. Also starts the thread that 335 * will call the renderer, which in turn causes the rendering to start. 336 * <p>This method should be called once and only once in the life-cycle of 337 * a GLSurfaceView. 338 * <p>The following GLSurfaceView methods can only be called <em>before</em> 339 * setRenderer is called: 340 * <ul> 341 * <li>{@link #setEGLConfigChooser(boolean)} 342 * <li>{@link #setEGLConfigChooser(EGLConfigChooser)} 343 * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)} 344 * </ul> 345 * <p> 346 * The following GLSurfaceView methods can only be called <em>after</em> 347 * setRenderer is called: 348 * <ul> 349 * <li>{@link #getRenderMode()} 350 * <li>{@link #onPause()} 351 * <li>{@link #onResume()} 352 * <li>{@link #queueEvent(Runnable)} 353 * <li>{@link #requestRender()} 354 * <li>{@link #setRenderMode(int)} 355 * </ul> 356 * 357 * @param renderer the renderer to use to perform OpenGL drawing. 358 */ 359 public void setRenderer(Renderer renderer) { 360 checkRenderThreadState(); 361 if (mEGLConfigChooser == null) { 362 mEGLConfigChooser = new SimpleEGLConfigChooser(true); 363 } 364 if (mEGLContextFactory == null) { 365 mEGLContextFactory = new DefaultContextFactory(); 366 } 367 if (mEGLWindowSurfaceFactory == null) { 368 mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory(); 369 } 370 mRenderer = renderer; 371 mGLThread = new GLThread(mThisWeakRef); 372 mGLThread.start(); 373 } 374 375 /** 376 * Install a custom EGLContextFactory. 377 * <p>If this method is 378 * called, it must be called before {@link #setRenderer(Renderer)} 379 * is called. 380 * <p> 381 * If this method is not called, then by default 382 * a context will be created with no shared context and 383 * with a null attribute list. 384 */ 385 public void setEGLContextFactory(EGLContextFactory factory) { 386 checkRenderThreadState(); 387 mEGLContextFactory = factory; 388 } 389 390 /** 391 * Install a custom EGLWindowSurfaceFactory. 392 * <p>If this method is 393 * called, it must be called before {@link #setRenderer(Renderer)} 394 * is called. 395 * <p> 396 * If this method is not called, then by default 397 * a window surface will be created with a null attribute list. 398 */ 399 public void setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory factory) { 400 checkRenderThreadState(); 401 mEGLWindowSurfaceFactory = factory; 402 } 403 404 /** 405 * Install a custom EGLConfigChooser. 406 * <p>If this method is 407 * called, it must be called before {@link #setRenderer(Renderer)} 408 * is called. 409 * <p> 410 * If no setEGLConfigChooser method is called, then by default the 411 * view will choose an EGLConfig that is compatible with the current 412 * android.view.Surface, with a depth buffer depth of 413 * at least 16 bits. 414 * @param configChooser 415 */ 416 public void setEGLConfigChooser(EGLConfigChooser configChooser) { 417 checkRenderThreadState(); 418 mEGLConfigChooser = configChooser; 419 } 420 421 /** 422 * Install a config chooser which will choose a config 423 * as close to 16-bit RGB as possible, with or without an optional depth 424 * buffer as close to 16-bits as possible. 425 * <p>If this method is 426 * called, it must be called before {@link #setRenderer(Renderer)} 427 * is called. 428 * <p> 429 * If no setEGLConfigChooser method is called, then by default the 430 * view will choose an RGB_888 surface with a depth buffer depth of 431 * at least 16 bits. 432 * 433 * @param needDepth 434 */ 435 public void setEGLConfigChooser(boolean needDepth) { 436 setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth)); 437 } 438 439 /** 440 * Install a config chooser which will choose a config 441 * with at least the specified depthSize and stencilSize, 442 * and exactly the specified redSize, greenSize, blueSize and alphaSize. 443 * <p>If this method is 444 * called, it must be called before {@link #setRenderer(Renderer)} 445 * is called. 446 * <p> 447 * If no setEGLConfigChooser method is called, then by default the 448 * view will choose an RGB_888 surface with a depth buffer depth of 449 * at least 16 bits. 450 * 451 */ 452 public void setEGLConfigChooser(int redSize, int greenSize, int blueSize, 453 int alphaSize, int depthSize, int stencilSize) { 454 setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize, 455 blueSize, alphaSize, depthSize, stencilSize)); 456 } 457 458 /** 459 * Inform the default EGLContextFactory and default EGLConfigChooser 460 * which EGLContext client version to pick. 461 * <p>Use this method to create an OpenGL ES 2.0-compatible context. 462 * Example: 463 * <pre class="prettyprint"> 464 * public MyView(Context context) { 465 * super(context); 466 * setEGLContextClientVersion(2); // Pick an OpenGL ES 2.0 context. 467 * setRenderer(new MyRenderer()); 468 * } 469 * </pre> 470 * <p>Note: Activities which require OpenGL ES 2.0 should indicate this by 471 * setting @lt;uses-feature android:glEsVersion="0x00020000" /> in the activity's 472 * AndroidManifest.xml file. 473 * <p>If this method is called, it must be called before {@link #setRenderer(Renderer)} 474 * is called. 475 * <p>This method only affects the behavior of the default EGLContexFactory and the 476 * default EGLConfigChooser. If 477 * {@link #setEGLContextFactory(EGLContextFactory)} has been called, then the supplied 478 * EGLContextFactory is responsible for creating an OpenGL ES 2.0-compatible context. 479 * If 480 * {@link #setEGLConfigChooser(EGLConfigChooser)} has been called, then the supplied 481 * EGLConfigChooser is responsible for choosing an OpenGL ES 2.0-compatible config. 482 * @param version The EGLContext client version to choose. Use 2 for OpenGL ES 2.0 483 */ 484 public void setEGLContextClientVersion(int version) { 485 checkRenderThreadState(); 486 mEGLContextClientVersion = version; 487 } 488 489 /** 490 * Set the rendering mode. When renderMode is 491 * RENDERMODE_CONTINUOUSLY, the renderer is called 492 * repeatedly to re-render the scene. When renderMode 493 * is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface 494 * is created, or when {@link #requestRender} is called. Defaults to RENDERMODE_CONTINUOUSLY. 495 * <p> 496 * Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system performance 497 * by allowing the GPU and CPU to idle when the view does not need to be updated. 498 * <p> 499 * This method can only be called after {@link #setRenderer(Renderer)} 500 * 501 * @param renderMode one of the RENDERMODE_X constants 502 * @see #RENDERMODE_CONTINUOUSLY 503 * @see #RENDERMODE_WHEN_DIRTY 504 */ 505 public void setRenderMode(int renderMode) { 506 mGLThread.setRenderMode(renderMode); 507 } 508 509 /** 510 * Get the current rendering mode. May be called 511 * from any thread. Must not be called before a renderer has been set. 512 * @return the current rendering mode. 513 * @see #RENDERMODE_CONTINUOUSLY 514 * @see #RENDERMODE_WHEN_DIRTY 515 */ 516 public int getRenderMode() { 517 return mGLThread.getRenderMode(); 518 } 519 520 /** 521 * Request that the renderer render a frame. 522 * This method is typically used when the render mode has been set to 523 * {@link #RENDERMODE_WHEN_DIRTY}, so that frames are only rendered on demand. 524 * May be called 525 * from any thread. Must not be called before a renderer has been set. 526 */ 527 public void requestRender() { 528 mGLThread.requestRender(); 529 } 530 531 /** 532 * This method is part of the SurfaceHolder.Callback interface, and is 533 * not normally called or subclassed by clients of GLSurfaceView. 534 */ 535 public void surfaceCreated(SurfaceHolder holder) { 536 mGLThread.surfaceCreated(); 537 } 538 539 /** 540 * This method is part of the SurfaceHolder.Callback interface, and is 541 * not normally called or subclassed by clients of GLSurfaceView. 542 */ 543 public void surfaceDestroyed(SurfaceHolder holder) { 544 // Surface will be destroyed when we return 545 mGLThread.surfaceDestroyed(); 546 } 547 548 /** 549 * This method is part of the SurfaceHolder.Callback interface, and is 550 * not normally called or subclassed by clients of GLSurfaceView. 551 */ 552 public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { 553 mGLThread.onWindowResize(w, h); 554 } 555 556 /** 557 * Inform the view that the activity is paused. The owner of this view must 558 * call this method when the activity is paused. Calling this method will 559 * pause the rendering thread. 560 * Must not be called before a renderer has been set. 561 */ 562 public void onPause() { 563 mGLThread.onPause(); 564 } 565 566 /** 567 * Inform the view that the activity is resumed. The owner of this view must 568 * call this method when the activity is resumed. Calling this method will 569 * recreate the OpenGL display and resume the rendering 570 * thread. 571 * Must not be called before a renderer has been set. 572 */ 573 public void onResume() { 574 mGLThread.onResume(); 575 } 576 577 /** 578 * Queue a runnable to be run on the GL rendering thread. This can be used 579 * to communicate with the Renderer on the rendering thread. 580 * Must not be called before a renderer has been set. 581 * @param r the runnable to be run on the GL rendering thread. 582 */ 583 public void queueEvent(Runnable r) { 584 mGLThread.queueEvent(r); 585 } 586 587 /** 588 * This method is used as part of the View class and is not normally 589 * called or subclassed by clients of GLSurfaceView. 590 */ 591 @Override 592 protected void onAttachedToWindow() { 593 super.onAttachedToWindow(); 594 if (LOG_ATTACH_DETACH) { 595 Log.d(TAG, "onAttachedToWindow reattach =" + mDetached); 596 } 597 if (mDetached && (mRenderer != null)) { 598 int renderMode = RENDERMODE_CONTINUOUSLY; 599 if (mGLThread != null) { 600 renderMode = mGLThread.getRenderMode(); 601 } 602 mGLThread = new GLThread(mThisWeakRef); 603 if (renderMode != RENDERMODE_CONTINUOUSLY) { 604 mGLThread.setRenderMode(renderMode); 605 } 606 mGLThread.start(); 607 } 608 mDetached = false; 609 } 610 611 /** 612 * This method is used as part of the View class and is not normally 613 * called or subclassed by clients of GLSurfaceView. 614 * Must not be called before a renderer has been set. 615 */ 616 @Override 617 protected void onDetachedFromWindow() { 618 if (LOG_ATTACH_DETACH) { 619 Log.d(TAG, "onDetachedFromWindow"); 620 } 621 if (mGLThread != null) { 622 mGLThread.requestExitAndWait(); 623 } 624 mDetached = true; 625 super.onDetachedFromWindow(); 626 } 627 628 // ---------------------------------------------------------------------- 629 630 /** 631 * An interface used to wrap a GL interface. 632 * <p>Typically 633 * used for implementing debugging and tracing on top of the default 634 * GL interface. You would typically use this by creating your own class 635 * that implemented all the GL methods by delegating to another GL instance. 636 * Then you could add your own behavior before or after calling the 637 * delegate. All the GLWrapper would do was instantiate and return the 638 * wrapper GL instance: 639 * <pre class="prettyprint"> 640 * class MyGLWrapper implements GLWrapper { 641 * GL wrap(GL gl) { 642 * return new MyGLImplementation(gl); 643 * } 644 * static class MyGLImplementation implements GL,GL10,GL11,... { 645 * ... 646 * } 647 * } 648 * </pre> 649 * @see #setGLWrapper(GLWrapper) 650 */ 651 public interface GLWrapper { 652 /** 653 * Wraps a gl interface in another gl interface. 654 * @param gl a GL interface that is to be wrapped. 655 * @return either the input argument or another GL object that wraps the input argument. 656 */ 657 GL wrap(GL gl); 658 } 659 660 // /** 661 // * A generic renderer interface. 662 // * <p> 663 // * The renderer is responsible for making OpenGL calls to render a frame. 664 // * <p> 665 // * GLSurfaceView clients typically create their own classes that implement 666 // * this interface, and then call {@link GLSurfaceView#setRenderer} to 667 // * register the renderer with the GLSurfaceView. 668 // * <p> 669 // * 670 // * <div class="special reference"> 671 // * <h3>Developer Guides</h3> 672 // * <p>For more information about how to use OpenGL, read the 673 // * <a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a> developer guide.</p> 674 // * </div> 675 // * 676 // * <h3>Threading</h3> 677 // * The renderer will be called on a separate thread, so that rendering 678 // * performance is decoupled from the UI thread. Clients typically need to 679 // * communicate with the renderer from the UI thread, because that's where 680 // * input events are received. Clients can communicate using any of the 681 // * standard Java techniques for cross-thread communication, or they can 682 // * use the {@link GLSurfaceView#queueEvent(Runnable)} convenience method. 683 // * <p> 684 // * <h3>EGL Context Lost</h3> 685 // * There are situations where the EGL rendering context will be lost. This 686 // * typically happens when device wakes up after going to sleep. When 687 // * the EGL context is lost, all OpenGL resources (such as textures) that are 688 // * associated with that context will be automatically deleted. In order to 689 // * keep rendering correctly, a renderer must recreate any lost resources 690 // * that it still needs. The {@link #onSurfaceCreated(GL10, EGLConfig)} method 691 // * is a convenient place to do this. 692 // * 693 // * 694 // * @see #setRenderer(Renderer) 695 // */ 696 // public interface Renderer { 697 // /** 698 // * Called when the surface is created or recreated. 699 // * <p> 700 // * Called when the rendering thread 701 // * starts and whenever the EGL context is lost. The EGL context will typically 702 // * be lost when the Android device awakes after going to sleep. 703 // * <p> 704 // * Since this method is called at the beginning of rendering, as well as 705 // * every time the EGL context is lost, this method is a convenient place to put 706 // * code to create resources that need to be created when the rendering 707 // * starts, and that need to be recreated when the EGL context is lost. 708 // * Textures are an example of a resource that you might want to create 709 // * here. 710 // * <p> 711 // * Note that when the EGL context is lost, all OpenGL resources associated 712 // * with that context will be automatically deleted. You do not need to call 713 // * the corresponding "glDelete" methods such as glDeleteTextures to 714 // * manually delete these lost resources. 715 // * <p> 716 // * @param gl the GL interface. Use <code>instanceof</code> to 717 // * test if the interface supports GL11 or higher interfaces. 718 // * @param config the EGLConfig of the created surface. Can be used 719 // * to create matching pbuffers. 720 // */ 721 // void onSurfaceCreated(GL10 gl, EGLConfig config); 722 // 723 // /** 724 // * Called when the surface changed size. 725 // * <p> 726 // * Called after the surface is created and whenever 727 // * the OpenGL ES surface size changes. 728 // * <p> 729 // * Typically you will set your viewport here. If your camera 730 // * is fixed then you could also set your projection matrix here: 731 // * <pre class="prettyprint"> 732 // * void onSurfaceChanged(GL10 gl, int width, int height) { 733 // * gl.glViewport(0, 0, width, height); 734 // * // for a fixed camera, set the projection too 735 // * float ratio = (float) width / height; 736 // * gl.glMatrixMode(GL10.GL_PROJECTION); 737 // * gl.glLoadIdentity(); 738 // * gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10); 739 // * } 740 // * </pre> 741 // * @param gl the GL interface. Use <code>instanceof</code> to 742 // * test if the interface supports GL11 or higher interfaces. 743 // * @param width 744 // * @param height 745 // */ 746 // void onSurfaceChanged(GL10 gl, int width, int height); 747 // 748 // /** 749 // * Called to draw the current frame. 750 // * <p> 751 // * This method is responsible for drawing the current frame. 752 // * <p> 753 // * The implementation of this method typically looks like this: 754 // * <pre class="prettyprint"> 755 // * void onDrawFrame(GL10 gl) { 756 // * gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); 757 // * //... other gl calls to render the scene ... 758 // * } 759 // * </pre> 760 // * @param gl the GL interface. Use <code>instanceof</code> to 761 // * test if the interface supports GL11 or higher interfaces. 762 // */ 763 // void onDrawFrame(GL10 gl); 764 // } 765 766 /** 767 * An interface for customizing the eglCreateContext and eglDestroyContext calls. 768 * <p> 769 * This interface must be implemented by clients wishing to call 770 * {@link GLSurfaceViewAPI18#setEGLContextFactory(EGLContextFactory)} 771 */ 772 public interface EGLContextFactory { 773 EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig); 774 void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context); 775 } 776 777 private class DefaultContextFactory implements EGLContextFactory { 778 private int EGL_CONTEXT_CLIENT_VERSION = 0x3098; 779 780 public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) { 781 final int EGL_CONTEXT_PRIORITY_LEVEL_IMG = 0x3100; 782 final int EGL_CONTEXT_PRIORITY_LOW_IMG = 0x3103; 783 int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion, 784 GLSurfaceView20.EGL_CONTEXT_PRIORITY_LEVEL_IMG, GLSurfaceView20.EGL_CONTEXT_PRIORITY_LOW_IMG, 785 EGL10.EGL_NONE}; 786 787 return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, 788 mEGLContextClientVersion != 0 ? attrib_list : null); 789 } 790 791 public void destroyContext(EGL10 egl, EGLDisplay display, 792 EGLContext context) { 793 if (!egl.eglDestroyContext(display, context)) { 794 Log.e("DefaultContextFactory", "display:" + display + " context: " + context); 795 if (LOG_THREADS) { 796 Log.i("DefaultContextFactory", "tid=" + Thread.currentThread().getId()); 797 } 798 EglHelper.throwEglException("eglDestroyContex", egl.eglGetError()); 799 } 800 } 801 } 802 803 /** 804 * An interface for customizing the eglCreateWindowSurface and eglDestroySurface calls. 805 * <p> 806 * This interface must be implemented by clients wishing to call 807 * {@link GLSurfaceViewAPI18#setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory)} 808 */ 809 public interface EGLWindowSurfaceFactory { 810 /** 811 * @return null if the surface cannot be constructed. 812 */ 813 EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config, 814 Object nativeWindow); 815 void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface); 816 } 817 818 private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory { 819 820 public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, 821 EGLConfig config, Object nativeWindow) { 822 EGLSurface result = null; 823 try { 824 result = egl.eglCreateWindowSurface(display, config, nativeWindow, null); 825 } catch (IllegalArgumentException e) { 826 // This exception indicates that the surface flinger surface 827 // is not valid. This can happen if the surface flinger surface has 828 // been torn down, but the application has not yet been 829 // notified via SurfaceHolder.Callback.surfaceDestroyed. 830 // In theory the application should be notified first, 831 // but in practice sometimes it is not. See b/4588890 832 Log.e(TAG, "eglCreateWindowSurface", e); 833 } 834 return result; 835 } 836 837 public void destroySurface(EGL10 egl, EGLDisplay display, 838 EGLSurface surface) { 839 egl.eglDestroySurface(display, surface); 840 } 841 } 842 843 // /** 844 // * An interface for choosing an EGLConfig configuration from a list of 845 // * potential configurations. 846 // * <p> 847 // * This interface must be implemented by clients wishing to call 848 // * {@link GLSurfaceView#setEGLConfigChooser(EGLConfigChooser)} 849 // */ 850 // public interface EGLConfigChooser { 851 // /** 852 // * Choose a configuration from the list. Implementors typically 853 // * implement this method by calling 854 // * {@link EGL10#eglChooseConfig} and iterating through the results. Please consult the 855 // * EGL specification available from The Khronos Group to learn how to call eglChooseConfig. 856 // * @param egl the EGL10 for the current display. 857 // * @param display the current display. 858 // * @return the chosen configuration. 859 // */ 860 // EGLConfig chooseConfig(EGL10 egl, EGLDisplay display); 861 // } 862 863 private abstract class BaseConfigChooser 864 implements EGLConfigChooser { 865 public BaseConfigChooser(int[] configSpec) { 866 mConfigSpec = filterConfigSpec(configSpec); 867 } 868 869 public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { 870 int[] num_config = new int[1]; 871 if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, 872 num_config)) { 873 throw new IllegalArgumentException("eglChooseConfig failed"); 874 } 875 876 int numConfigs = num_config[0]; 877 878 if (numConfigs <= 0) { 879 throw new IllegalArgumentException( 880 "No configs match configSpec"); 881 } 882 883 EGLConfig[] configs = new EGLConfig[numConfigs]; 884 if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, 885 num_config)) { 886 throw new IllegalArgumentException("eglChooseConfig#2 failed"); 887 } 888 EGLConfig config = chooseConfig(egl, display, configs); 889 if (config == null) { 890 throw new IllegalArgumentException("No config chosen"); 891 } 892 return config; 893 } 894 895 abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, 896 EGLConfig[] configs); 897 898 protected int[] mConfigSpec; 899 900 private int[] filterConfigSpec(int[] configSpec) { 901 if (mEGLContextClientVersion != 2) { 902 return configSpec; 903 } 904 /* We know none of the subclasses define EGL_RENDERABLE_TYPE. 905 * And we know the configSpec is well formed. 906 */ 907 int len = configSpec.length; 908 int[] newConfigSpec = new int[len + 2]; 909 System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1); 910 newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE; 911 newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */ 912 newConfigSpec[len+1] = EGL10.EGL_NONE; 913 return newConfigSpec; 914 } 915 } 916 917 /** 918 * Choose a configuration with exactly the specified r,g,b,a sizes, 919 * and at least the specified depth and stencil sizes. 920 */ 921 private class ComponentSizeChooser extends BaseConfigChooser { 922 public ComponentSizeChooser(int redSize, int greenSize, int blueSize, 923 int alphaSize, int depthSize, int stencilSize) { 924 super(new int[] { 925 EGL10.EGL_RED_SIZE, redSize, 926 EGL10.EGL_GREEN_SIZE, greenSize, 927 EGL10.EGL_BLUE_SIZE, blueSize, 928 EGL10.EGL_ALPHA_SIZE, alphaSize, 929 EGL10.EGL_DEPTH_SIZE, depthSize, 930 EGL10.EGL_STENCIL_SIZE, stencilSize, 931 EGL10.EGL_NONE}); 932 mValue = new int[1]; 933 mRedSize = redSize; 934 mGreenSize = greenSize; 935 mBlueSize = blueSize; 936 mAlphaSize = alphaSize; 937 mDepthSize = depthSize; 938 mStencilSize = stencilSize; 939 } 940 941 @Override 942 public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, 943 EGLConfig[] configs) { 944 for (EGLConfig config : configs) { 945 int d = findConfigAttrib(egl, display, config, 946 EGL10.EGL_DEPTH_SIZE, 0); 947 int s = findConfigAttrib(egl, display, config, 948 EGL10.EGL_STENCIL_SIZE, 0); 949 if ((d >= mDepthSize) && (s >= mStencilSize)) { 950 int r = findConfigAttrib(egl, display, config, 951 EGL10.EGL_RED_SIZE, 0); 952 int g = findConfigAttrib(egl, display, config, 953 EGL10.EGL_GREEN_SIZE, 0); 954 int b = findConfigAttrib(egl, display, config, 955 EGL10.EGL_BLUE_SIZE, 0); 956 int a = findConfigAttrib(egl, display, config, 957 EGL10.EGL_ALPHA_SIZE, 0); 958 if ((r == mRedSize) && (g == mGreenSize) 959 && (b == mBlueSize) && (a == mAlphaSize)) { 960 return config; 961 } 962 } 963 } 964 return null; 965 } 966 967 private int findConfigAttrib(EGL10 egl, EGLDisplay display, 968 EGLConfig config, int attribute, int defaultValue) { 969 970 if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) { 971 return mValue[0]; 972 } 973 return defaultValue; 974 } 975 976 private int[] mValue; 977 // Subclasses can adjust these values: 978 protected int mRedSize; 979 protected int mGreenSize; 980 protected int mBlueSize; 981 protected int mAlphaSize; 982 protected int mDepthSize; 983 protected int mStencilSize; 984 } 985 986 /** 987 * This class will choose a RGB_888 surface with 988 * or without a depth buffer. 989 * 990 */ 991 private class SimpleEGLConfigChooser extends ComponentSizeChooser { 992 public SimpleEGLConfigChooser(boolean withDepthBuffer) { 993 super(8, 8, 8, 0, withDepthBuffer ? 16 : 0, 0); 994 } 995 } 996 997 /** 998 * An EGL helper class. 999 */ 1000 1001 private static class EglHelper { 1002 public EglHelper(WeakReference<GLSurfaceViewAPI18> glSurfaceViewWeakRef) { 1003 mGLSurfaceViewWeakRef = glSurfaceViewWeakRef; 1004 } 1005 1006 /** 1007 * Initialize EGL for a given configuration spec. 1008 */ 1009 public void start() { 1010 if (LOG_EGL) { 1011 Log.w("EglHelper", "start() tid=" + Thread.currentThread().getId()); 1012 } 1013 /* 1014 * Get an EGL instance 1015 */ 1016 mEgl = (EGL10) EGLContext.getEGL(); 1017 1018 /* 1019 * Get to the default display. 1020 */ 1021 mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); 1022 1023 if (mEglDisplay == EGL10.EGL_NO_DISPLAY) { 1024 throw new RuntimeException("eglGetDisplay failed"); 1025 } 1026 1027 /* 1028 * We can now initialize EGL for that display 1029 */ 1030 int[] version = new int[2]; 1031 if(!mEgl.eglInitialize(mEglDisplay, version)) { 1032 throw new RuntimeException("eglInitialize failed"); 1033 } 1034 GLSurfaceViewAPI18 view = mGLSurfaceViewWeakRef.get(); 1035 if (view == null) { 1036 mEglConfig = null; 1037 mEglContext = null; 1038 } else { 1039 mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay); 1040 1041 /* 1042 * Create an EGL context. We want to do this as rarely as we can, because an 1043 * EGL context is a somewhat heavy object. 1044 */ 1045 mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig); 1046 } 1047 if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) { 1048 mEglContext = null; 1049 throwEglException("createContext"); 1050 } 1051 if (LOG_EGL) { 1052 Log.w("EglHelper", "createContext " + mEglContext + " tid=" + Thread.currentThread().getId()); 1053 } 1054 1055 mEglSurface = null; 1056 } 1057 1058 /** 1059 * Create an egl surface for the current SurfaceHolder surface. If a surface 1060 * already exists, destroy it before creating the new surface. 1061 * 1062 * @return true if the surface was created successfully. 1063 */ 1064 public boolean createSurface() { 1065 if (LOG_EGL) { 1066 Log.w("EglHelper", "createSurface() tid=" + Thread.currentThread().getId()); 1067 } 1068 /* 1069 * Check preconditions. 1070 */ 1071 if (mEgl == null) { 1072 throw new RuntimeException("egl not initialized"); 1073 } 1074 if (mEglDisplay == null) { 1075 throw new RuntimeException("eglDisplay not initialized"); 1076 } 1077 if (mEglConfig == null) { 1078 throw new RuntimeException("mEglConfig not initialized"); 1079 } 1080 1081 /* 1082 * The window size has changed, so we need to create a new 1083 * surface. 1084 */ 1085 destroySurfaceImp(); 1086 1087 /* 1088 * Create an EGL surface we can render into. 1089 */ 1090 GLSurfaceViewAPI18 view = mGLSurfaceViewWeakRef.get(); 1091 if (view != null) { 1092 mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl, 1093 mEglDisplay, mEglConfig, view.getHolder()); 1094 } else { 1095 mEglSurface = null; 1096 } 1097 1098 if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) { 1099 int error = mEgl.eglGetError(); 1100 if (error == EGL10.EGL_BAD_NATIVE_WINDOW) { 1101 Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW."); 1102 } 1103 return false; 1104 } 1105 1106 /* 1107 * Before we can issue GL commands, we need to make sure 1108 * the context is current and bound to a surface. 1109 */ 1110 if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 1111 /* 1112 * Could not make the context current, probably because the underlying 1113 * SurfaceView surface has been destroyed. 1114 */ 1115 logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError()); 1116 return false; 1117 } 1118 1119 return true; 1120 } 1121 1122 /** 1123 * Create a GL object for the current EGL context. 1124 */ 1125 GL createGL() { 1126 1127 GL gl = mEglContext.getGL(); 1128 GLSurfaceViewAPI18 view = mGLSurfaceViewWeakRef.get(); 1129 if (view != null) { 1130 if (view.mGLWrapper != null) { 1131 gl = view.mGLWrapper.wrap(gl); 1132 } 1133 1134 if ((view.mDebugFlags & (DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS)) != 0) { 1135 int configFlags = 0; 1136 Writer log = null; 1137 if ((view.mDebugFlags & DEBUG_CHECK_GL_ERROR) != 0) { 1138 configFlags |= GLDebugHelper.CONFIG_CHECK_GL_ERROR; 1139 } 1140 if ((view.mDebugFlags & DEBUG_LOG_GL_CALLS) != 0) { 1141 log = new LogWriter(); 1142 } 1143 gl = GLDebugHelper.wrap(gl, configFlags, log); 1144 } 1145 } 1146 return gl; 1147 } 1148 1149 /** 1150 * Display the current render surface. 1151 * @return the EGL error code from eglSwapBuffers. 1152 */ 1153 public int swap() { 1154 if (! mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) { 1155 return mEgl.eglGetError(); 1156 } 1157 return EGL10.EGL_SUCCESS; 1158 } 1159 1160 public void destroySurface() { 1161 if (LOG_EGL) { 1162 Log.w("EglHelper", "destroySurface() tid=" + Thread.currentThread().getId()); 1163 } 1164 destroySurfaceImp(); 1165 } 1166 1167 private void destroySurfaceImp() { 1168 if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) { 1169 mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, 1170 EGL10.EGL_NO_SURFACE, 1171 EGL10.EGL_NO_CONTEXT); 1172 GLSurfaceViewAPI18 view = mGLSurfaceViewWeakRef.get(); 1173 if (view != null) { 1174 view.mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface); 1175 } 1176 mEglSurface = null; 1177 } 1178 } 1179 1180 public void finish() { 1181 if (LOG_EGL) { 1182 Log.w("EglHelper", "finish() tid=" + Thread.currentThread().getId()); 1183 } 1184 if (mEglContext != null) { 1185 GLSurfaceViewAPI18 view = mGLSurfaceViewWeakRef.get(); 1186 if (view != null) { 1187 view.mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext); 1188 } 1189 mEglContext = null; 1190 } 1191 if (mEglDisplay != null) { 1192 mEgl.eglTerminate(mEglDisplay); 1193 mEglDisplay = null; 1194 } 1195 } 1196 1197 private void throwEglException(String function) { 1198 throwEglException(function, mEgl.eglGetError()); 1199 } 1200 1201 public static void throwEglException(String function, int error) { 1202 String message = formatEglError(function, error); 1203 if (LOG_THREADS) { 1204 Log.e("EglHelper", "throwEglException tid=" + Thread.currentThread().getId() + " " 1205 + message); 1206 } 1207 throw new RuntimeException(message); 1208 } 1209 1210 public static void logEglErrorAsWarning(String tag, String function, int error) { 1211 Log.w(tag, formatEglError(function, error)); 1212 } 1213 1214 // Method taken from class android.opengl.EGLLogWrapper which is package-private 1215 private static String getErrorString(int error) { 1216 switch (error) { 1217 case EGL10.EGL_SUCCESS: 1218 return "EGL_SUCCESS"; 1219 case EGL10.EGL_NOT_INITIALIZED: 1220 return "EGL_NOT_INITIALIZED"; 1221 case EGL10.EGL_BAD_ACCESS: 1222 return "EGL_BAD_ACCESS"; 1223 case EGL10.EGL_BAD_ALLOC: 1224 return "EGL_BAD_ALLOC"; 1225 case EGL10.EGL_BAD_ATTRIBUTE: 1226 return "EGL_BAD_ATTRIBUTE"; 1227 case EGL10.EGL_BAD_CONFIG: 1228 return "EGL_BAD_CONFIG"; 1229 case EGL10.EGL_BAD_CONTEXT: 1230 return "EGL_BAD_CONTEXT"; 1231 case EGL10.EGL_BAD_CURRENT_SURFACE: 1232 return "EGL_BAD_CURRENT_SURFACE"; 1233 case EGL10.EGL_BAD_DISPLAY: 1234 return "EGL_BAD_DISPLAY"; 1235 case EGL10.EGL_BAD_MATCH: 1236 return "EGL_BAD_MATCH"; 1237 case EGL10.EGL_BAD_NATIVE_PIXMAP: 1238 return "EGL_BAD_NATIVE_PIXMAP"; 1239 case EGL10.EGL_BAD_NATIVE_WINDOW: 1240 return "EGL_BAD_NATIVE_WINDOW"; 1241 case EGL10.EGL_BAD_PARAMETER: 1242 return "EGL_BAD_PARAMETER"; 1243 case EGL10.EGL_BAD_SURFACE: 1244 return "EGL_BAD_SURFACE"; 1245 case EGL11.EGL_CONTEXT_LOST: 1246 return "EGL_CONTEXT_LOST"; 1247 default: 1248 return "0x" + Integer.toHexString(error); 1249 } 1250 } 1251 1252 public static String formatEglError(String function, int error) { 1253 return function + " failed: " + getErrorString(error); 1254 } 1255 1256 private WeakReference<GLSurfaceViewAPI18> mGLSurfaceViewWeakRef; 1257 EGL10 mEgl; 1258 EGLDisplay mEglDisplay; 1259 EGLSurface mEglSurface; 1260 EGLConfig mEglConfig; 1261 EGLContext mEglContext; 1262 1263 } 1264 1265 /** 1266 * A generic GL Thread. Takes care of initializing EGL and GL. Delegates 1267 * to a Renderer instance to do the actual drawing. Can be configured to 1268 * render continuously or on request. 1269 * 1270 * All potentially blocking synchronization is done through the 1271 * sGLThreadManager object. This avoids multiple-lock ordering issues. 1272 * 1273 */ 1274 static class GLThread extends Thread { 1275 GLThread(WeakReference<GLSurfaceViewAPI18> glSurfaceViewWeakRef) { 1276 super(); 1277 mWidth = 0; 1278 mHeight = 0; 1279 mRequestRender = true; 1280 mRenderMode = RENDERMODE_CONTINUOUSLY; 1281 mGLSurfaceViewWeakRef = glSurfaceViewWeakRef; 1282 } 1283 1284 @Override 1285 public void run() { 1286 setName("GLThread " + getId()); 1287 if (LOG_THREADS) { 1288 Log.i("GLThread", "starting tid=" + getId()); 1289 } 1290 1291 try { 1292 guardedRun(); 1293 } catch (InterruptedException e) { 1294 // fall thru and exit normally 1295 } finally { 1296 sGLThreadManager.threadExiting(this); 1297 } 1298 } 1299 1300 /* 1301 * This private method should only be called inside a 1302 * synchronized(sGLThreadManager) block. 1303 */ 1304 private void stopEglSurfaceLocked() { 1305 if (mHaveEglSurface) { 1306 mHaveEglSurface = false; 1307 mEglHelper.destroySurface(); 1308 } 1309 } 1310 1311 /* 1312 * This private method should only be called inside a 1313 * synchronized(sGLThreadManager) block. 1314 */ 1315 private void stopEglContextLocked() { 1316 if (mHaveEglContext) { 1317 mEglHelper.finish(); 1318 mHaveEglContext = false; 1319 sGLThreadManager.releaseEglContextLocked(this); 1320 } 1321 } 1322 private void guardedRun() throws InterruptedException { 1323 mEglHelper = new EglHelper(mGLSurfaceViewWeakRef); 1324 mHaveEglContext = false; 1325 mHaveEglSurface = false; 1326 try { 1327 GL10 gl = null; 1328 boolean createEglContext = false; 1329 boolean createEglSurface = false; 1330 boolean createGlInterface = false; 1331 boolean lostEglContext = false; 1332 boolean sizeChanged = false; 1333 boolean wantRenderNotification = false; 1334 boolean doRenderNotification = false; 1335 boolean askedToReleaseEglContext = false; 1336 int w = 0; 1337 int h = 0; 1338 Runnable event = null; 1339 1340 while (true) { 1341 synchronized (sGLThreadManager) { 1342 while (true) { 1343 if (mShouldExit) { 1344 return; 1345 } 1346 1347 if (! mEventQueue.isEmpty()) { 1348 event = mEventQueue.remove(0); 1349 break; 1350 } 1351 1352 // Update the pause state. 1353 boolean pausing = false; 1354 if (mPaused != mRequestPaused) { 1355 pausing = mRequestPaused; 1356 mPaused = mRequestPaused; 1357 sGLThreadManager.notifyAll(); 1358 if (LOG_PAUSE_RESUME) { 1359 Log.i("GLThread", "mPaused is now " + mPaused + " tid=" + getId()); 1360 } 1361 } 1362 1363 // Do we need to give up the EGL context? 1364 if (mShouldReleaseEglContext) { 1365 if (LOG_SURFACE) { 1366 Log.i("GLThread", "releasing EGL context because asked to tid=" + getId()); 1367 } 1368 stopEglSurfaceLocked(); 1369 stopEglContextLocked(); 1370 mShouldReleaseEglContext = false; 1371 askedToReleaseEglContext = true; 1372 } 1373 1374 // Have we lost the EGL context? 1375 if (lostEglContext) { 1376 stopEglSurfaceLocked(); 1377 stopEglContextLocked(); 1378 lostEglContext = false; 1379 } 1380 1381 // When pausing, release the EGL surface: 1382 if (pausing && mHaveEglSurface) { 1383 if (LOG_SURFACE) { 1384 Log.i("GLThread", "releasing EGL surface because paused tid=" + getId()); 1385 } 1386 stopEglSurfaceLocked(); 1387 } 1388 1389 // When pausing, optionally release the EGL Context: 1390 if (pausing && mHaveEglContext) { 1391 GLSurfaceViewAPI18 view = mGLSurfaceViewWeakRef.get(); 1392 boolean preserveEglContextOnPause = view == null ? 1393 false : view.mPreserveEGLContextOnPause; 1394 if (!preserveEglContextOnPause || sGLThreadManager.shouldReleaseEGLContextWhenPausing()) { 1395 stopEglContextLocked(); 1396 if (LOG_SURFACE) { 1397 Log.i("GLThread", "releasing EGL context because paused tid=" + getId()); 1398 } 1399 } 1400 } 1401 1402 // When pausing, optionally terminate EGL: 1403 if (pausing) { 1404 if (sGLThreadManager.shouldTerminateEGLWhenPausing()) { 1405 mEglHelper.finish(); 1406 if (LOG_SURFACE) { 1407 Log.i("GLThread", "terminating EGL because paused tid=" + getId()); 1408 } 1409 } 1410 } 1411 1412 // Have we lost the SurfaceView surface? 1413 if ((! mHasSurface) && (! mWaitingForSurface)) { 1414 if (LOG_SURFACE) { 1415 Log.i("GLThread", "noticed surfaceView surface lost tid=" + getId()); 1416 } 1417 if (mHaveEglSurface) { 1418 stopEglSurfaceLocked(); 1419 } 1420 mWaitingForSurface = true; 1421 mSurfaceIsBad = false; 1422 sGLThreadManager.notifyAll(); 1423 } 1424 1425 // Have we acquired the surface view surface? 1426 if (mHasSurface && mWaitingForSurface) { 1427 if (LOG_SURFACE) { 1428 Log.i("GLThread", "noticed surfaceView surface acquired tid=" + getId()); 1429 } 1430 mWaitingForSurface = false; 1431 sGLThreadManager.notifyAll(); 1432 } 1433 1434 if (doRenderNotification) { 1435 if (LOG_SURFACE) { 1436 Log.i("GLThread", "sending render notification tid=" + getId()); 1437 } 1438 wantRenderNotification = false; 1439 doRenderNotification = false; 1440 mRenderComplete = true; 1441 sGLThreadManager.notifyAll(); 1442 } 1443 1444 // Ready to draw? 1445 if (readyToDraw()) { 1446 1447 // If we don't have an EGL context, try to acquire one. 1448 if (! mHaveEglContext) { 1449 if (askedToReleaseEglContext) { 1450 askedToReleaseEglContext = false; 1451 } else if (sGLThreadManager.tryAcquireEglContextLocked(this)) { 1452 try { 1453 mEglHelper.start(); 1454 } catch (RuntimeException t) { 1455 sGLThreadManager.releaseEglContextLocked(this); 1456 throw t; 1457 } 1458 mHaveEglContext = true; 1459 createEglContext = true; 1460 1461 sGLThreadManager.notifyAll(); 1462 } 1463 } 1464 1465 if (mHaveEglContext && !mHaveEglSurface) { 1466 mHaveEglSurface = true; 1467 createEglSurface = true; 1468 createGlInterface = true; 1469 sizeChanged = true; 1470 } 1471 1472 if (mHaveEglSurface) { 1473 if (mSizeChanged) { 1474 sizeChanged = true; 1475 w = mWidth; 1476 h = mHeight; 1477 wantRenderNotification = true; 1478 if (LOG_SURFACE) { 1479 Log.i("GLThread", 1480 "noticing that we want render notification tid=" 1481 + getId()); 1482 } 1483 1484 // Destroy and recreate the EGL surface. 1485 createEglSurface = true; 1486 1487 mSizeChanged = false; 1488 } 1489 mRequestRender = false; 1490 sGLThreadManager.notifyAll(); 1491 break; 1492 } 1493 } 1494 1495 // By design, this is the only place in a GLThread thread where we wait(). 1496 if (LOG_THREADS) { 1497 Log.i("GLThread", "waiting tid=" + getId() 1498 + " mHaveEglContext: " + mHaveEglContext 1499 + " mHaveEglSurface: " + mHaveEglSurface 1500 + " mFinishedCreatingEglSurface: " + mFinishedCreatingEglSurface 1501 + " mPaused: " + mPaused 1502 + " mHasSurface: " + mHasSurface 1503 + " mSurfaceIsBad: " + mSurfaceIsBad 1504 + " mWaitingForSurface: " + mWaitingForSurface 1505 + " mWidth: " + mWidth 1506 + " mHeight: " + mHeight 1507 + " mRequestRender: " + mRequestRender 1508 + " mRenderMode: " + mRenderMode); 1509 } 1510 sGLThreadManager.wait(); 1511 } 1512 } // end of synchronized(sGLThreadManager) 1513 1514 if (event != null) { 1515 event.run(); 1516 event = null; 1517 continue; 1518 } 1519 1520 if (createEglSurface) { 1521 if (LOG_SURFACE) { 1522 Log.w("GLThread", "egl createSurface"); 1523 } 1524 if (mEglHelper.createSurface()) { 1525 synchronized(sGLThreadManager) { 1526 mFinishedCreatingEglSurface = true; 1527 sGLThreadManager.notifyAll(); 1528 } 1529 } else { 1530 synchronized(sGLThreadManager) { 1531 mFinishedCreatingEglSurface = true; 1532 mSurfaceIsBad = true; 1533 sGLThreadManager.notifyAll(); 1534 } 1535 continue; 1536 } 1537 createEglSurface = false; 1538 } 1539 1540 if (createGlInterface) { 1541 gl = (GL10) mEglHelper.createGL(); 1542 1543 sGLThreadManager.checkGLDriver(gl); 1544 createGlInterface = false; 1545 } 1546 1547 if (createEglContext) { 1548 if (LOG_RENDERER) { 1549 Log.w("GLThread", "onSurfaceCreated"); 1550 } 1551 GLSurfaceViewAPI18 view = mGLSurfaceViewWeakRef.get(); 1552 if (view != null) { 1553 view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig); 1554 } 1555 createEglContext = false; 1556 } 1557 1558 if (sizeChanged) { 1559 if (LOG_RENDERER) { 1560 Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")"); 1561 } 1562 GLSurfaceViewAPI18 view = mGLSurfaceViewWeakRef.get(); 1563 if (view != null) { 1564 view.mRenderer.onSurfaceChanged(gl, w, h); 1565 } 1566 sizeChanged = false; 1567 } 1568 1569 if (LOG_RENDERER_DRAW_FRAME) { 1570 Log.w("GLThread", "onDrawFrame tid=" + getId()); 1571 } 1572 { 1573 GLSurfaceViewAPI18 view = mGLSurfaceViewWeakRef.get(); 1574 if (view != null) { 1575 view.mRenderer.onDrawFrame(gl); 1576 } 1577 } 1578 int swapError = mEglHelper.swap(); 1579 switch (swapError) { 1580 case EGL10.EGL_SUCCESS: 1581 break; 1582 case EGL11.EGL_CONTEXT_LOST: 1583 if (LOG_SURFACE) { 1584 Log.i("GLThread", "egl context lost tid=" + getId()); 1585 } 1586 lostEglContext = true; 1587 break; 1588 default: 1589 // Other errors typically mean that the current surface is bad, 1590 // probably because the SurfaceView surface has been destroyed, 1591 // but we haven't been notified yet. 1592 // Log the error to help developers understand why rendering stopped. 1593 EglHelper.logEglErrorAsWarning("GLThread", "eglSwapBuffers", swapError); 1594 1595 synchronized(sGLThreadManager) { 1596 mSurfaceIsBad = true; 1597 sGLThreadManager.notifyAll(); 1598 } 1599 break; 1600 } 1601 1602 if (wantRenderNotification) { 1603 doRenderNotification = true; 1604 } 1605 } 1606 1607 } finally { 1608 /* 1609 * clean-up everything... 1610 */ 1611 synchronized (sGLThreadManager) { 1612 stopEglSurfaceLocked(); 1613 stopEglContextLocked(); 1614 } 1615 } 1616 } 1617 1618 public boolean ableToDraw() { 1619 return mHaveEglContext && mHaveEglSurface && readyToDraw(); 1620 } 1621 1622 private boolean readyToDraw() { 1623 return (!mPaused) && mHasSurface && (!mSurfaceIsBad) 1624 && (mWidth > 0) && (mHeight > 0) 1625 && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY)); 1626 } 1627 1628 public void setRenderMode(int renderMode) { 1629 if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) { 1630 throw new IllegalArgumentException("renderMode"); 1631 } 1632 synchronized(sGLThreadManager) { 1633 mRenderMode = renderMode; 1634 sGLThreadManager.notifyAll(); 1635 } 1636 } 1637 1638 public int getRenderMode() { 1639 synchronized(sGLThreadManager) { 1640 return mRenderMode; 1641 } 1642 } 1643 1644 public void requestRender() { 1645 synchronized(sGLThreadManager) { 1646 mRequestRender = true; 1647 sGLThreadManager.notifyAll(); 1648 } 1649 } 1650 1651 public void surfaceCreated() { 1652 synchronized(sGLThreadManager) { 1653 if (LOG_THREADS) { 1654 Log.i("GLThread", "surfaceCreated tid=" + getId()); 1655 } 1656 mHasSurface = true; 1657 mFinishedCreatingEglSurface = false; 1658 sGLThreadManager.notifyAll(); 1659 while (mWaitingForSurface 1660 && !mFinishedCreatingEglSurface 1661 && !mExited) { 1662 try { 1663 sGLThreadManager.wait(); 1664 } catch (InterruptedException e) { 1665 Thread.currentThread().interrupt(); 1666 } 1667 } 1668 } 1669 } 1670 1671 public void surfaceDestroyed() { 1672 synchronized(sGLThreadManager) { 1673 if (LOG_THREADS) { 1674 Log.i("GLThread", "surfaceDestroyed tid=" + getId()); 1675 } 1676 mHasSurface = false; 1677 sGLThreadManager.notifyAll(); 1678 while((!mWaitingForSurface) && (!mExited)) { 1679 try { 1680 sGLThreadManager.wait(); 1681 } catch (InterruptedException e) { 1682 Thread.currentThread().interrupt(); 1683 } 1684 } 1685 } 1686 } 1687 1688 public void onPause() { 1689 synchronized (sGLThreadManager) { 1690 if (LOG_PAUSE_RESUME) { 1691 Log.i("GLThread", "onPause tid=" + getId()); 1692 } 1693 mRequestPaused = true; 1694 sGLThreadManager.notifyAll(); 1695 while ((! mExited) && (! mPaused)) { 1696 if (LOG_PAUSE_RESUME) { 1697 Log.i("Main thread", "onPause waiting for mPaused."); 1698 } 1699 try { 1700 sGLThreadManager.wait(); 1701 } catch (InterruptedException ex) { 1702 Thread.currentThread().interrupt(); 1703 } 1704 } 1705 } 1706 } 1707 1708 public void onResume() { 1709 synchronized (sGLThreadManager) { 1710 if (LOG_PAUSE_RESUME) { 1711 Log.i("GLThread", "onResume tid=" + getId()); 1712 } 1713 mRequestPaused = false; 1714 mRequestRender = true; 1715 mRenderComplete = false; 1716 sGLThreadManager.notifyAll(); 1717 while ((! mExited) && mPaused && (!mRenderComplete)) { 1718 if (LOG_PAUSE_RESUME) { 1719 Log.i("Main thread", "onResume waiting for !mPaused."); 1720 } 1721 try { 1722 sGLThreadManager.wait(); 1723 } catch (InterruptedException ex) { 1724 Thread.currentThread().interrupt(); 1725 } 1726 } 1727 } 1728 } 1729 1730 public void onWindowResize(int w, int h) { 1731 synchronized (sGLThreadManager) { 1732 mWidth = w; 1733 mHeight = h; 1734 mSizeChanged = true; 1735 mRequestRender = true; 1736 mRenderComplete = false; 1737 sGLThreadManager.notifyAll(); 1738 1739 // Wait for thread to react to resize and render a frame 1740 while (! mExited && !mPaused && !mRenderComplete 1741 && ableToDraw()) { 1742 if (LOG_SURFACE) { 1743 Log.i("Main thread", "onWindowResize waiting for render complete from tid=" + getId()); 1744 } 1745 try { 1746 sGLThreadManager.wait(); 1747 } catch (InterruptedException ex) { 1748 Thread.currentThread().interrupt(); 1749 } 1750 } 1751 } 1752 } 1753 1754 public void requestExitAndWait() { 1755 // don't call this from GLThread thread or it is a guaranteed 1756 // deadlock! 1757 synchronized(sGLThreadManager) { 1758 mShouldExit = true; 1759 sGLThreadManager.notifyAll(); 1760 while (! mExited) { 1761 try { 1762 sGLThreadManager.wait(); 1763 } catch (InterruptedException ex) { 1764 Thread.currentThread().interrupt(); 1765 } 1766 } 1767 } 1768 } 1769 1770 public void requestReleaseEglContextLocked() { 1771 mShouldReleaseEglContext = true; 1772 sGLThreadManager.notifyAll(); 1773 } 1774 1775 /** 1776 * Queue an "event" to be run on the GL rendering thread. 1777 * @param r the runnable to be run on the GL rendering thread. 1778 */ 1779 public void queueEvent(Runnable r) { 1780 if (r == null) { 1781 throw new IllegalArgumentException("r must not be null"); 1782 } 1783 synchronized(sGLThreadManager) { 1784 mEventQueue.add(r); 1785 sGLThreadManager.notifyAll(); 1786 } 1787 } 1788 1789 // Once the thread is started, all accesses to the following member 1790 // variables are protected by the sGLThreadManager monitor 1791 private boolean mShouldExit; 1792 private boolean mExited; 1793 private boolean mRequestPaused; 1794 private boolean mPaused; 1795 private boolean mHasSurface; 1796 private boolean mSurfaceIsBad; 1797 private boolean mWaitingForSurface; 1798 private boolean mHaveEglContext; 1799 private boolean mHaveEglSurface; 1800 private boolean mFinishedCreatingEglSurface; 1801 private boolean mShouldReleaseEglContext; 1802 private int mWidth; 1803 private int mHeight; 1804 private int mRenderMode; 1805 private boolean mRequestRender; 1806 private boolean mRenderComplete; 1807 private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>(); 1808 private boolean mSizeChanged = true; 1809 1810 // End of member variables protected by the sGLThreadManager monitor. 1811 1812 private EglHelper mEglHelper; 1813 1814 /** 1815 * Set once at thread construction time, nulled out when the parent view is garbage 1816 * called. This weak reference allows the GLSurfaceView to be garbage collected while 1817 * the GLThread is still alive. 1818 */ 1819 private WeakReference<GLSurfaceViewAPI18> mGLSurfaceViewWeakRef; 1820 1821 } 1822 1823 static class LogWriter extends Writer { 1824 1825 @Override public void close() { 1826 flushBuilder(); 1827 } 1828 1829 @Override public void flush() { 1830 flushBuilder(); 1831 } 1832 1833 @Override public void write(char[] buf, int offset, int count) { 1834 for(int i = 0; i < count; i++) { 1835 char c = buf[offset + i]; 1836 if ( c == '\n') { 1837 flushBuilder(); 1838 } 1839 else { 1840 mBuilder.append(c); 1841 } 1842 } 1843 } 1844 1845 private void flushBuilder() { 1846 if (mBuilder.length() > 0) { 1847 Log.v("GLSurfaceView", mBuilder.toString()); 1848 mBuilder.delete(0, mBuilder.length()); 1849 } 1850 } 1851 1852 private StringBuilder mBuilder = new StringBuilder(); 1853 } 1854 1855 1856 private void checkRenderThreadState() { 1857 if (mGLThread != null) { 1858 throw new IllegalStateException( 1859 "setRenderer has already been called for this instance."); 1860 } 1861 } 1862 1863 private static class GLThreadManager { 1864 private static String TAG = "GLThreadManager"; 1865 1866 public synchronized void threadExiting(GLThread thread) { 1867 if (LOG_THREADS) { 1868 Log.i("GLThread", "exiting tid=" + thread.getId()); 1869 } 1870 thread.mExited = true; 1871 if (mEglOwner == thread) { 1872 mEglOwner = null; 1873 } 1874 notifyAll(); 1875 } 1876 1877 /* 1878 * Tries once to acquire the right to use an EGL 1879 * context. Does not block. Requires that we are already 1880 * in the sGLThreadManager monitor when this is called. 1881 * 1882 * @return true if the right to use an EGL context was acquired. 1883 */ 1884 public boolean tryAcquireEglContextLocked(GLThread thread) { 1885 if (mEglOwner == thread || mEglOwner == null) { 1886 mEglOwner = thread; 1887 notifyAll(); 1888 return true; 1889 } 1890 checkGLESVersion(); 1891 if (mMultipleGLESContextsAllowed) { 1892 return true; 1893 } 1894 // Notify the owning thread that it should release the context. 1895 // TODO: implement a fairness policy. Currently 1896 // if the owning thread is drawing continuously it will just 1897 // reacquire the EGL context. 1898 if (mEglOwner != null) { 1899 mEglOwner.requestReleaseEglContextLocked(); 1900 } 1901 return false; 1902 } 1903 1904 /* 1905 * Releases the EGL context. Requires that we are already in the 1906 * sGLThreadManager monitor when this is called. 1907 */ 1908 public void releaseEglContextLocked(GLThread thread) { 1909 if (mEglOwner == thread) { 1910 mEglOwner = null; 1911 } 1912 notifyAll(); 1913 } 1914 1915 public synchronized boolean shouldReleaseEGLContextWhenPausing() { 1916 // Release the EGL context when pausing even if 1917 // the hardware supports multiple EGL contexts. 1918 // Otherwise the device could run out of EGL contexts. 1919 return mLimitedGLESContexts; 1920 } 1921 1922 public synchronized boolean shouldTerminateEGLWhenPausing() { 1923 checkGLESVersion(); 1924 return !mMultipleGLESContextsAllowed; 1925 } 1926 1927 public synchronized void checkGLDriver(GL10 gl) { 1928 if (! mGLESDriverCheckComplete) { 1929 checkGLESVersion(); 1930 String renderer = gl.glGetString(GL10.GL_RENDERER); 1931 if (mGLESVersion < kGLES_20) { 1932 mMultipleGLESContextsAllowed = 1933 ! renderer.startsWith(kMSM7K_RENDERER_PREFIX); 1934 notifyAll(); 1935 } 1936 mLimitedGLESContexts = !mMultipleGLESContextsAllowed; 1937 if (LOG_SURFACE) { 1938 Log.w(TAG, "checkGLDriver renderer = \"" + renderer + "\" multipleContextsAllowed = " 1939 + mMultipleGLESContextsAllowed 1940 + " mLimitedGLESContexts = " + mLimitedGLESContexts); 1941 } 1942 mGLESDriverCheckComplete = true; 1943 } 1944 } 1945 1946 private void checkGLESVersion() { 1947 if (! mGLESVersionCheckComplete) { 1948 // mGLESVersion = SystemProperties.getInt( 1949 // "ro.opengles.version", 1950 // ConfigurationInfo.GL_ES_VERSION_UNDEFINED); 1951 // SystemProperties is a hidden class that is not part of the public SDK 1952 // so we force GL ES version to 2.0 1953 mGLESVersion = kGLES_20; 1954 if (mGLESVersion >= kGLES_20) { 1955 mMultipleGLESContextsAllowed = true; 1956 } 1957 if (LOG_SURFACE) { 1958 Log.w(TAG, "checkGLESVersion mGLESVersion =" + 1959 " " + mGLESVersion + " mMultipleGLESContextsAllowed = " + mMultipleGLESContextsAllowed); 1960 } 1961 mGLESVersionCheckComplete = true; 1962 } 1963 } 1964 1965 /** 1966 * This check was required for some pre-Android-3.0 hardware. Android 3.0 provides 1967 * support for hardware-accelerated views, therefore multiple EGL contexts are 1968 * supported on all Android 3.0+ EGL drivers. 1969 */ 1970 private boolean mGLESVersionCheckComplete; 1971 private int mGLESVersion; 1972 private boolean mGLESDriverCheckComplete; 1973 private boolean mMultipleGLESContextsAllowed; 1974 private boolean mLimitedGLESContexts; 1975 private static final int kGLES_20 = 0x20000; 1976 private static final String kMSM7K_RENDERER_PREFIX = 1977 "Q3Dimension MSM7500 "; 1978 private GLThread mEglOwner; 1979 } 1980 1981 private static final GLThreadManager sGLThreadManager = new GLThreadManager(); 1982 1983 private final WeakReference<GLSurfaceViewAPI18> mThisWeakRef = 1984 new WeakReference<GLSurfaceViewAPI18>(this); 1985 private GLThread mGLThread; 1986 private Renderer mRenderer; 1987 private boolean mDetached; 1988 private EGLConfigChooser mEGLConfigChooser; 1989 private EGLContextFactory mEGLContextFactory; 1990 private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory; 1991 private GLWrapper mGLWrapper; 1992 private int mDebugFlags; 1993 private int mEGLContextClientVersion; 1994 private boolean mPreserveEGLContextOnPause; 1995 } 1996