GLSurfaceView.java revision 5aa3adcf304ec3767b3c22128acb23240d8d0bab
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.util.AttributeSet; 33import android.util.Log; 34import android.view.SurfaceHolder; 35import android.view.SurfaceView; 36 37/** 38 * An implementation of SurfaceView that uses the dedicated surface for 39 * displaying OpenGL rendering. 40 * <p> 41 * A GLSurfaceView provides the following features: 42 * <p> 43 * <ul> 44 * <li>Manages a surface, which is a special piece of memory that can be 45 * composited into the Android view system. 46 * <li>Manages an EGL display, which enables OpenGL to render into a surface. 47 * <li>Accepts a user-provided Renderer object that does the actual rendering. 48 * <li>Renders on a dedicated thread to decouple rendering performance from the 49 * UI thread. 50 * <li>Supports both on-demand and continuous rendering. 51 * <li>Optionally wraps, traces, and/or error-checks the renderer's OpenGL calls. 52 * </ul> 53 * 54 * <h3>Using GLSurfaceView</h3> 55 * <p> 56 * Typically you use GLSurfaceView by subclassing it and overriding one or more of the 57 * View system input event methods. If your application does not need to override event 58 * methods then GLSurfaceView can be used as-is. For the most part 59 * GLSurfaceView behavior is customized by calling "set" methods rather than by subclassing. 60 * For example, unlike a regular View, drawing is delegated to a separate Renderer object which 61 * is registered with the GLSurfaceView 62 * using the {@link #setRenderer(Renderer)} call. 63 * <p> 64 * <h3>Initializing GLSurfaceView</h3> 65 * All you have to do to initialize a GLSurfaceView is call {@link #setRenderer(Renderer)}. 66 * However, if desired, you can modify the default behavior of GLSurfaceView by calling one or 67 * more of these methods before calling setRenderer: 68 * <ul> 69 * <li>{@link #setDebugFlags(int)} 70 * <li>{@link #setEGLConfigChooser(boolean)} 71 * <li>{@link #setEGLConfigChooser(EGLConfigChooser)} 72 * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)} 73 * <li>{@link #setGLWrapper(GLWrapper)} 74 * </ul> 75 * <p> 76 * <h4>Choosing an EGL Configuration</h4> 77 * A given Android device may support multiple possible types of drawing surfaces. 78 * The available surfaces may differ in how may channels of data are present, as 79 * well as how many bits are allocated to each channel. Therefore, the first thing 80 * GLSurfaceView has to do when starting to render is choose what type of surface to use. 81 * <p> 82 * By default GLSurfaceView chooses an available surface that's closest to a 16-bit R5G6B5 surface 83 * with a 16-bit depth buffer and no stencil. If you would prefer a different surface (for example, 84 * if you do not need a depth buffer) you can override the default behavior by calling one of the 85 * setEGLConfigChooser methods. 86 * <p> 87 * <h4>Debug Behavior</h4> 88 * You can optionally modify the behavior of GLSurfaceView by calling 89 * one or more of the debugging methods {@link #setDebugFlags(int)}, 90 * and {@link #setGLWrapper}. These methods may be called before and/or after setRenderer, but 91 * typically they are called before setRenderer so that they take effect immediately. 92 * <p> 93 * <h4>Setting a Renderer</h4> 94 * Finally, you must call {@link #setRenderer} to register a {@link Renderer}. 95 * The renderer is 96 * responsible for doing the actual OpenGL rendering. 97 * <p> 98 * <h3>Rendering Mode</h3> 99 * Once the renderer is set, you can control whether the renderer draws 100 * continuously or on-demand by calling 101 * {@link #setRenderMode}. The default is continuous rendering. 102 * <p> 103 * <h3>Activity Life-cycle</h3> 104 * A GLSurfaceView must be notified when the activity is paused and resumed. GLSurfaceView clients 105 * are required to call {@link #onPause()} when the activity pauses and 106 * {@link #onResume()} when the activity resumes. These calls allow GLSurfaceView to 107 * pause and resume the rendering thread, and also allow GLSurfaceView to release and recreate 108 * the OpenGL display. 109 * <p> 110 * <h3>Handling events</h3> 111 * <p> 112 * To handle an event you will typically subclass GLSurfaceView and override the 113 * appropriate method, just as you would with any other View. However, when handling 114 * the event, you may need to communicate with the Renderer object 115 * that's running in the rendering thread. You can do this using any 116 * standard Java cross-thread communication mechanism. In addition, 117 * one relatively easy way to communicate with your renderer is 118 * to call 119 * {@link #queueEvent(Runnable)}. For example: 120 * <pre class="prettyprint"> 121 * class MyGLSurfaceView extends GLSurfaceView { 122 * 123 * private MyRenderer mMyRenderer; 124 * 125 * public void start() { 126 * mMyRenderer = ...; 127 * setRenderer(mMyRenderer); 128 * } 129 * 130 * public boolean onKeyDown(int keyCode, KeyEvent event) { 131 * if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { 132 * queueEvent(new Runnable() { 133 * // This method will be called on the rendering 134 * // thread: 135 * public void run() { 136 * mMyRenderer.handleDpadCenter(); 137 * }}); 138 * return true; 139 * } 140 * return super.onKeyDown(keyCode, event); 141 * } 142 * } 143 * </pre> 144 * 145 */ 146public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback { 147 private final static boolean LOG_THREADS = false; 148 private final static boolean LOG_SURFACE = false; 149 private final static boolean LOG_RENDERER = false; 150 // Work-around for bug 2263168 151 private final static boolean DRAW_TWICE_AFTER_SIZE_CHANGED = true; 152 /** 153 * The renderer only renders 154 * when the surface is created, or when {@link #requestRender} is called. 155 * 156 * @see #getRenderMode() 157 * @see #setRenderMode(int) 158 */ 159 public final static int RENDERMODE_WHEN_DIRTY = 0; 160 /** 161 * The renderer is called 162 * continuously to re-render the scene. 163 * 164 * @see #getRenderMode() 165 * @see #setRenderMode(int) 166 * @see #requestRender() 167 */ 168 public final static int RENDERMODE_CONTINUOUSLY = 1; 169 170 /** 171 * Check glError() after every GL call and throw an exception if glError indicates 172 * that an error has occurred. This can be used to help track down which OpenGL ES call 173 * is causing an error. 174 * 175 * @see #getDebugFlags 176 * @see #setDebugFlags 177 */ 178 public final static int DEBUG_CHECK_GL_ERROR = 1; 179 180 /** 181 * Log GL calls to the system log at "verbose" level with tag "GLSurfaceView". 182 * 183 * @see #getDebugFlags 184 * @see #setDebugFlags 185 */ 186 public final static int DEBUG_LOG_GL_CALLS = 2; 187 188 /** 189 * Standard View constructor. In order to render something, you 190 * must call {@link #setRenderer} to register a renderer. 191 */ 192 public GLSurfaceView(Context context) { 193 super(context); 194 init(); 195 } 196 197 /** 198 * Standard View constructor. In order to render something, you 199 * must call {@link #setRenderer} to register a renderer. 200 */ 201 public GLSurfaceView(Context context, AttributeSet attrs) { 202 super(context, attrs); 203 init(); 204 } 205 206 private void init() { 207 // Install a SurfaceHolder.Callback so we get notified when the 208 // underlying surface is created and destroyed 209 SurfaceHolder holder = getHolder(); 210 holder.addCallback(this); 211 } 212 213 /** 214 * Set the glWrapper. If the glWrapper is not null, its 215 * {@link GLWrapper#wrap(GL)} method is called 216 * whenever a surface is created. A GLWrapper can be used to wrap 217 * the GL object that's passed to the renderer. Wrapping a GL 218 * object enables examining and modifying the behavior of the 219 * GL calls made by the renderer. 220 * <p> 221 * Wrapping is typically used for debugging purposes. 222 * <p> 223 * The default value is null. 224 * @param glWrapper the new GLWrapper 225 */ 226 public void setGLWrapper(GLWrapper glWrapper) { 227 mGLWrapper = glWrapper; 228 } 229 230 /** 231 * Set the debug flags to a new value. The value is 232 * constructed by OR-together zero or more 233 * of the DEBUG_CHECK_* constants. The debug flags take effect 234 * whenever a surface is created. The default value is zero. 235 * @param debugFlags the new debug flags 236 * @see #DEBUG_CHECK_GL_ERROR 237 * @see #DEBUG_LOG_GL_CALLS 238 */ 239 public void setDebugFlags(int debugFlags) { 240 mDebugFlags = debugFlags; 241 } 242 243 /** 244 * Get the current value of the debug flags. 245 * @return the current value of the debug flags. 246 */ 247 public int getDebugFlags() { 248 return mDebugFlags; 249 } 250 251 /** 252 * Set the renderer associated with this view. Also starts the thread that 253 * will call the renderer, which in turn causes the rendering to start. 254 * <p>This method should be called once and only once in the life-cycle of 255 * a GLSurfaceView. 256 * <p>The following GLSurfaceView methods can only be called <em>before</em> 257 * setRenderer is called: 258 * <ul> 259 * <li>{@link #setEGLConfigChooser(boolean)} 260 * <li>{@link #setEGLConfigChooser(EGLConfigChooser)} 261 * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)} 262 * </ul> 263 * <p> 264 * The following GLSurfaceView methods can only be called <em>after</em> 265 * setRenderer is called: 266 * <ul> 267 * <li>{@link #getRenderMode()} 268 * <li>{@link #onPause()} 269 * <li>{@link #onResume()} 270 * <li>{@link #queueEvent(Runnable)} 271 * <li>{@link #requestRender()} 272 * <li>{@link #setRenderMode(int)} 273 * </ul> 274 * 275 * @param renderer the renderer to use to perform OpenGL drawing. 276 */ 277 public void setRenderer(Renderer renderer) { 278 checkRenderThreadState(); 279 if (mEGLConfigChooser == null) { 280 mEGLConfigChooser = new SimpleEGLConfigChooser(true); 281 } 282 if (mEGLContextFactory == null) { 283 mEGLContextFactory = new DefaultContextFactory(); 284 } 285 if (mEGLWindowSurfaceFactory == null) { 286 mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory(); 287 } 288 mGLThread = new GLThread(renderer); 289 mGLThread.start(); 290 } 291 292 /** 293 * Install a custom EGLContextFactory. 294 * <p>If this method is 295 * called, it must be called before {@link #setRenderer(Renderer)} 296 * is called. 297 * <p> 298 * If this method is not called, then by default 299 * a context will be created with no shared context and 300 * with a null attribute list. 301 */ 302 public void setEGLContextFactory(EGLContextFactory factory) { 303 checkRenderThreadState(); 304 mEGLContextFactory = factory; 305 } 306 307 /** 308 * Install a custom EGLWindowSurfaceFactory. 309 * <p>If this method is 310 * called, it must be called before {@link #setRenderer(Renderer)} 311 * is called. 312 * <p> 313 * If this method is not called, then by default 314 * a window surface will be created with a null attribute list. 315 */ 316 public void setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory factory) { 317 checkRenderThreadState(); 318 mEGLWindowSurfaceFactory = factory; 319 } 320 321 /** 322 * Install a custom EGLConfigChooser. 323 * <p>If this method is 324 * called, it must be called before {@link #setRenderer(Renderer)} 325 * is called. 326 * <p> 327 * If no setEGLConfigChooser method is called, then by default the 328 * view will choose a config as close to 16-bit RGB as possible, with 329 * a depth buffer as close to 16 bits as possible. 330 * @param configChooser 331 */ 332 public void setEGLConfigChooser(EGLConfigChooser configChooser) { 333 checkRenderThreadState(); 334 mEGLConfigChooser = configChooser; 335 } 336 337 /** 338 * Install a config chooser which will choose a config 339 * as close to 16-bit RGB as possible, with or without an optional depth 340 * buffer as close to 16-bits as possible. 341 * <p>If this method is 342 * called, it must be called before {@link #setRenderer(Renderer)} 343 * is called. 344 * <p> 345 * If no setEGLConfigChooser method is called, then by default the 346 * view will choose a config as close to 16-bit RGB as possible, with 347 * a depth buffer as close to 16 bits as possible. 348 * 349 * @param needDepth 350 */ 351 public void setEGLConfigChooser(boolean needDepth) { 352 setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth)); 353 } 354 355 /** 356 * Install a config chooser which will choose a config 357 * with at least the specified component sizes, and as close 358 * to the specified component sizes as possible. 359 * <p>If this method is 360 * called, it must be called before {@link #setRenderer(Renderer)} 361 * is called. 362 * <p> 363 * If no setEGLConfigChooser method is called, then by default the 364 * view will choose a config as close to 16-bit RGB as possible, with 365 * a depth buffer as close to 16 bits as possible. 366 * 367 */ 368 public void setEGLConfigChooser(int redSize, int greenSize, int blueSize, 369 int alphaSize, int depthSize, int stencilSize) { 370 setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize, 371 blueSize, alphaSize, depthSize, stencilSize)); 372 } 373 /** 374 * Set the rendering mode. When renderMode is 375 * RENDERMODE_CONTINUOUSLY, the renderer is called 376 * repeatedly to re-render the scene. When renderMode 377 * is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface 378 * is created, or when {@link #requestRender} is called. Defaults to RENDERMODE_CONTINUOUSLY. 379 * <p> 380 * Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system performance 381 * by allowing the GPU and CPU to idle when the view does not need to be updated. 382 * <p> 383 * This method can only be called after {@link #setRenderer(Renderer)} 384 * 385 * @param renderMode one of the RENDERMODE_X constants 386 * @see #RENDERMODE_CONTINUOUSLY 387 * @see #RENDERMODE_WHEN_DIRTY 388 */ 389 public void setRenderMode(int renderMode) { 390 mGLThread.setRenderMode(renderMode); 391 } 392 393 /** 394 * Get the current rendering mode. May be called 395 * from any thread. Must not be called before a renderer has been set. 396 * @return the current rendering mode. 397 * @see #RENDERMODE_CONTINUOUSLY 398 * @see #RENDERMODE_WHEN_DIRTY 399 */ 400 public int getRenderMode() { 401 return mGLThread.getRenderMode(); 402 } 403 404 /** 405 * Request that the renderer render a frame. 406 * This method is typically used when the render mode has been set to 407 * {@link #RENDERMODE_WHEN_DIRTY}, so that frames are only rendered on demand. 408 * May be called 409 * from any thread. Must not be called before a renderer has been set. 410 */ 411 public void requestRender() { 412 mGLThread.requestRender(); 413 } 414 415 /** 416 * This method is part of the SurfaceHolder.Callback interface, and is 417 * not normally called or subclassed by clients of GLSurfaceView. 418 */ 419 public void surfaceCreated(SurfaceHolder holder) { 420 mGLThread.surfaceCreated(); 421 } 422 423 /** 424 * This method is part of the SurfaceHolder.Callback interface, and is 425 * not normally called or subclassed by clients of GLSurfaceView. 426 */ 427 public void surfaceDestroyed(SurfaceHolder holder) { 428 // Surface will be destroyed when we return 429 mGLThread.surfaceDestroyed(); 430 } 431 432 /** 433 * This method is part of the SurfaceHolder.Callback interface, and is 434 * not normally called or subclassed by clients of GLSurfaceView. 435 */ 436 public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { 437 mGLThread.onWindowResize(w, h); 438 } 439 440 /** 441 * Inform the view that the activity is paused. The owner of this view must 442 * call this method when the activity is paused. Calling this method will 443 * pause the rendering thread. 444 * Must not be called before a renderer has been set. 445 */ 446 public void onPause() { 447 mGLThread.onPause(); 448 } 449 450 /** 451 * Inform the view that the activity is resumed. The owner of this view must 452 * call this method when the activity is resumed. Calling this method will 453 * recreate the OpenGL display and resume the rendering 454 * thread. 455 * Must not be called before a renderer has been set. 456 */ 457 public void onResume() { 458 mGLThread.onResume(); 459 } 460 461 /** 462 * Queue a runnable to be run on the GL rendering thread. This can be used 463 * to communicate with the Renderer on the rendering thread. 464 * Must not be called before a renderer has been set. 465 * @param r the runnable to be run on the GL rendering thread. 466 */ 467 public void queueEvent(Runnable r) { 468 mGLThread.queueEvent(r); 469 } 470 471 /** 472 * This method is used as part of the View class and is not normally 473 * called or subclassed by clients of GLSurfaceView. 474 * Must not be called before a renderer has been set. 475 */ 476 @Override 477 protected void onDetachedFromWindow() { 478 super.onDetachedFromWindow(); 479 mGLThread.requestExitAndWait(); 480 } 481 482 // ---------------------------------------------------------------------- 483 484 /** 485 * An interface used to wrap a GL interface. 486 * <p>Typically 487 * used for implementing debugging and tracing on top of the default 488 * GL interface. You would typically use this by creating your own class 489 * that implemented all the GL methods by delegating to another GL instance. 490 * Then you could add your own behavior before or after calling the 491 * delegate. All the GLWrapper would do was instantiate and return the 492 * wrapper GL instance: 493 * <pre class="prettyprint"> 494 * class MyGLWrapper implements GLWrapper { 495 * GL wrap(GL gl) { 496 * return new MyGLImplementation(gl); 497 * } 498 * static class MyGLImplementation implements GL,GL10,GL11,... { 499 * ... 500 * } 501 * } 502 * </pre> 503 * @see #setGLWrapper(GLWrapper) 504 */ 505 public interface GLWrapper { 506 /** 507 * Wraps a gl interface in another gl interface. 508 * @param gl a GL interface that is to be wrapped. 509 * @return either the input argument or another GL object that wraps the input argument. 510 */ 511 GL wrap(GL gl); 512 } 513 514 /** 515 * A generic renderer interface. 516 * <p> 517 * The renderer is responsible for making OpenGL calls to render a frame. 518 * <p> 519 * GLSurfaceView clients typically create their own classes that implement 520 * this interface, and then call {@link GLSurfaceView#setRenderer} to 521 * register the renderer with the GLSurfaceView. 522 * <p> 523 * <h3>Threading</h3> 524 * The renderer will be called on a separate thread, so that rendering 525 * performance is decoupled from the UI thread. Clients typically need to 526 * communicate with the renderer from the UI thread, because that's where 527 * input events are received. Clients can communicate using any of the 528 * standard Java techniques for cross-thread communication, or they can 529 * use the {@link GLSurfaceView#queueEvent(Runnable)} convenience method. 530 * <p> 531 * <h3>EGL Context Lost</h3> 532 * There are situations where the EGL rendering context will be lost. This 533 * typically happens when device wakes up after going to sleep. When 534 * the EGL context is lost, all OpenGL resources (such as textures) that are 535 * associated with that context will be automatically deleted. In order to 536 * keep rendering correctly, a renderer must recreate any lost resources 537 * that it still needs. The {@link #onSurfaceCreated(GL10, EGLConfig)} method 538 * is a convenient place to do this. 539 * 540 * 541 * @see #setRenderer(Renderer) 542 */ 543 public interface Renderer { 544 /** 545 * Called when the surface is created or recreated. 546 * <p> 547 * Called when the rendering thread 548 * starts and whenever the EGL context is lost. The context will typically 549 * be lost when the Android device awakes after going to sleep. 550 * <p> 551 * Since this method is called at the beginning of rendering, as well as 552 * every time the EGL context is lost, this method is a convenient place to put 553 * code to create resources that need to be created when the rendering 554 * starts, and that need to be recreated when the EGL context is lost. 555 * Textures are an example of a resource that you might want to create 556 * here. 557 * <p> 558 * Note that when the EGL context is lost, all OpenGL resources associated 559 * with that context will be automatically deleted. You do not need to call 560 * the corresponding "glDelete" methods such as glDeleteTextures to 561 * manually delete these lost resources. 562 * <p> 563 * @param gl the GL interface. Use <code>instanceof</code> to 564 * test if the interface supports GL11 or higher interfaces. 565 * @param config the EGLConfig of the created surface. Can be used 566 * to create matching pbuffers. 567 */ 568 void onSurfaceCreated(GL10 gl, EGLConfig config); 569 570 /** 571 * Called when the surface changed size. 572 * <p> 573 * Called after the surface is created and whenever 574 * the OpenGL ES surface size changes. 575 * <p> 576 * Typically you will set your viewport here. If your camera 577 * is fixed then you could also set your projection matrix here: 578 * <pre class="prettyprint"> 579 * void onSurfaceChanged(GL10 gl, int width, int height) { 580 * gl.glViewport(0, 0, width, height); 581 * // for a fixed camera, set the projection too 582 * float ratio = (float) width / height; 583 * gl.glMatrixMode(GL10.GL_PROJECTION); 584 * gl.glLoadIdentity(); 585 * gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10); 586 * } 587 * </pre> 588 * @param gl the GL interface. Use <code>instanceof</code> to 589 * test if the interface supports GL11 or higher interfaces. 590 * @param width 591 * @param height 592 */ 593 void onSurfaceChanged(GL10 gl, int width, int height); 594 595 /** 596 * Called to draw the current frame. 597 * <p> 598 * This method is responsible for drawing the current frame. 599 * <p> 600 * The implementation of this method typically looks like this: 601 * <pre class="prettyprint"> 602 * void onDrawFrame(GL10 gl) { 603 * gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); 604 * //... other gl calls to render the scene ... 605 * } 606 * </pre> 607 * @param gl the GL interface. Use <code>instanceof</code> to 608 * test if the interface supports GL11 or higher interfaces. 609 */ 610 void onDrawFrame(GL10 gl); 611 } 612 613 /** 614 * An interface for customizing the eglCreateContext and eglDestroyContext calls. 615 * <p> 616 * This interface must be implemented by clients wishing to call 617 * {@link GLSurfaceView#setEGLContextFactory(EGLContextFactory)} 618 */ 619 public interface EGLContextFactory { 620 EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig); 621 void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context); 622 } 623 624 private static class DefaultContextFactory implements EGLContextFactory { 625 626 public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) { 627 return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, null); 628 } 629 630 public void destroyContext(EGL10 egl, EGLDisplay display, 631 EGLContext context) { 632 egl.eglDestroyContext(display, context); 633 } 634 } 635 636 /** 637 * An interface for customizing the eglCreateWindowSurface and eglDestroySurface calls. 638 * <p> 639 * This interface must be implemented by clients wishing to call 640 * {@link GLSurfaceView#setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory)} 641 */ 642 public interface EGLWindowSurfaceFactory { 643 EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config, 644 Object nativeWindow); 645 void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface); 646 } 647 648 private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory { 649 650 public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, 651 EGLConfig config, Object nativeWindow) { 652 return egl.eglCreateWindowSurface(display, config, nativeWindow, null); 653 } 654 655 public void destroySurface(EGL10 egl, EGLDisplay display, 656 EGLSurface surface) { 657 egl.eglDestroySurface(display, surface); 658 } 659 } 660 661 /** 662 * An interface for choosing an EGLConfig configuration from a list of 663 * potential configurations. 664 * <p> 665 * This interface must be implemented by clients wishing to call 666 * {@link GLSurfaceView#setEGLConfigChooser(EGLConfigChooser)} 667 */ 668 public interface EGLConfigChooser { 669 /** 670 * Choose a configuration from the list. Implementors typically 671 * implement this method by calling 672 * {@link EGL10#eglChooseConfig} and iterating through the results. Please consult the 673 * EGL specification available from The Khronos Group to learn how to call eglChooseConfig. 674 * @param egl the EGL10 for the current display. 675 * @param display the current display. 676 * @return the chosen configuration. 677 */ 678 EGLConfig chooseConfig(EGL10 egl, EGLDisplay display); 679 } 680 681 private static abstract class BaseConfigChooser 682 implements EGLConfigChooser { 683 public BaseConfigChooser(int[] configSpec) { 684 mConfigSpec = configSpec; 685 } 686 public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { 687 int[] num_config = new int[1]; 688 egl.eglChooseConfig(display, mConfigSpec, null, 0, num_config); 689 690 int numConfigs = num_config[0]; 691 692 if (numConfigs <= 0) { 693 throw new IllegalArgumentException( 694 "No configs match configSpec"); 695 } 696 697 EGLConfig[] configs = new EGLConfig[numConfigs]; 698 egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, 699 num_config); 700 EGLConfig config = chooseConfig(egl, display, configs); 701 if (config == null) { 702 throw new IllegalArgumentException("No config chosen"); 703 } 704 return config; 705 } 706 707 abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, 708 EGLConfig[] configs); 709 710 protected int[] mConfigSpec; 711 } 712 713 private static class ComponentSizeChooser extends BaseConfigChooser { 714 public ComponentSizeChooser(int redSize, int greenSize, int blueSize, 715 int alphaSize, int depthSize, int stencilSize) { 716 super(new int[] { 717 EGL10.EGL_RED_SIZE, redSize, 718 EGL10.EGL_GREEN_SIZE, greenSize, 719 EGL10.EGL_BLUE_SIZE, blueSize, 720 EGL10.EGL_ALPHA_SIZE, alphaSize, 721 EGL10.EGL_DEPTH_SIZE, depthSize, 722 EGL10.EGL_STENCIL_SIZE, stencilSize, 723 EGL10.EGL_NONE}); 724 mValue = new int[1]; 725 mRedSize = redSize; 726 mGreenSize = greenSize; 727 mBlueSize = blueSize; 728 mAlphaSize = alphaSize; 729 mDepthSize = depthSize; 730 mStencilSize = stencilSize; 731 } 732 733 @Override 734 public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, 735 EGLConfig[] configs) { 736 EGLConfig closestConfig = null; 737 int closestDistance = 1000; 738 for(EGLConfig config : configs) { 739 int d = findConfigAttrib(egl, display, config, 740 EGL10.EGL_DEPTH_SIZE, 0); 741 int s = findConfigAttrib(egl, display, config, 742 EGL10.EGL_STENCIL_SIZE, 0); 743 if (d >= mDepthSize && s>= mStencilSize) { 744 int r = findConfigAttrib(egl, display, config, 745 EGL10.EGL_RED_SIZE, 0); 746 int g = findConfigAttrib(egl, display, config, 747 EGL10.EGL_GREEN_SIZE, 0); 748 int b = findConfigAttrib(egl, display, config, 749 EGL10.EGL_BLUE_SIZE, 0); 750 int a = findConfigAttrib(egl, display, config, 751 EGL10.EGL_ALPHA_SIZE, 0); 752 int distance = Math.abs(r - mRedSize) 753 + Math.abs(g - mGreenSize) 754 + Math.abs(b - mBlueSize) 755 + Math.abs(a - mAlphaSize); 756 if (distance < closestDistance) { 757 closestDistance = distance; 758 closestConfig = config; 759 } 760 } 761 } 762 return closestConfig; 763 } 764 765 private int findConfigAttrib(EGL10 egl, EGLDisplay display, 766 EGLConfig config, int attribute, int defaultValue) { 767 768 if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) { 769 return mValue[0]; 770 } 771 return defaultValue; 772 } 773 774 private int[] mValue; 775 // Subclasses can adjust these values: 776 protected int mRedSize; 777 protected int mGreenSize; 778 protected int mBlueSize; 779 protected int mAlphaSize; 780 protected int mDepthSize; 781 protected int mStencilSize; 782 } 783 784 /** 785 * This class will choose a supported surface as close to 786 * RGB565 as possible, with or without a depth buffer. 787 * 788 */ 789 private static class SimpleEGLConfigChooser extends ComponentSizeChooser { 790 public SimpleEGLConfigChooser(boolean withDepthBuffer) { 791 super(4, 4, 4, 0, withDepthBuffer ? 16 : 0, 0); 792 // Adjust target values. This way we'll accept a 4444 or 793 // 555 buffer if there's no 565 buffer available. 794 mRedSize = 5; 795 mGreenSize = 6; 796 mBlueSize = 5; 797 } 798 } 799 800 /** 801 * An EGL helper class. 802 */ 803 804 private class EglHelper { 805 public EglHelper() { 806 807 } 808 809 /** 810 * Initialize EGL for a given configuration spec. 811 * @param configSpec 812 */ 813 public void start(){ 814 /* 815 * Get an EGL instance 816 */ 817 mEgl = (EGL10) EGLContext.getEGL(); 818 819 /* 820 * Get to the default display. 821 */ 822 mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); 823 824 /* 825 * We can now initialize EGL for that display 826 */ 827 int[] version = new int[2]; 828 mEgl.eglInitialize(mEglDisplay, version); 829 mEglConfig = mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay); 830 831 /* 832 * Create an OpenGL ES context. This must be done only once, an 833 * OpenGL context is a somewhat heavy object. 834 */ 835 mEglContext = mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig); 836 if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) { 837 throw new RuntimeException("createContext failed"); 838 } 839 840 mEglSurface = null; 841 } 842 843 /* 844 * React to the creation of a new surface by creating and returning an 845 * OpenGL interface that renders to that surface. 846 */ 847 public GL createSurface(SurfaceHolder holder) { 848 /* 849 * The window size has changed, so we need to create a new 850 * surface. 851 */ 852 if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) { 853 854 /* 855 * Unbind and destroy the old EGL surface, if 856 * there is one. 857 */ 858 mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, 859 EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); 860 mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface); 861 } 862 863 /* 864 * Create an EGL surface we can render into. 865 */ 866 mEglSurface = mEGLWindowSurfaceFactory.createWindowSurface(mEgl, 867 mEglDisplay, mEglConfig, holder); 868 869 if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) { 870 throwEglException("createWindowSurface"); 871 } 872 873 /* 874 * Before we can issue GL commands, we need to make sure 875 * the context is current and bound to a surface. 876 */ 877 if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 878 throwEglException("eglMakeCurrent"); 879 } 880 881 GL gl = mEglContext.getGL(); 882 if (mGLWrapper != null) { 883 gl = mGLWrapper.wrap(gl); 884 } 885 886 if ((mDebugFlags & (DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS)) != 0) { 887 int configFlags = 0; 888 Writer log = null; 889 if ((mDebugFlags & DEBUG_CHECK_GL_ERROR) != 0) { 890 configFlags |= GLDebugHelper.CONFIG_CHECK_GL_ERROR; 891 } 892 if ((mDebugFlags & DEBUG_LOG_GL_CALLS) != 0) { 893 log = new LogWriter(); 894 } 895 gl = GLDebugHelper.wrap(gl, configFlags, log); 896 } 897 return gl; 898 } 899 900 /** 901 * Display the current render surface. 902 * @return false if the context has been lost. 903 */ 904 public boolean swap() { 905 mEgl.eglSwapBuffers(mEglDisplay, mEglSurface); 906 907 /* 908 * Always check for EGL_CONTEXT_LOST, which means the context 909 * and all associated data were lost (For instance because 910 * the device went to sleep). We need to sleep until we 911 * get a new surface. 912 */ 913 return mEgl.eglGetError() != EGL11.EGL_CONTEXT_LOST; 914 } 915 916 public void destroySurface() { 917 if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) { 918 mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, 919 EGL10.EGL_NO_SURFACE, 920 EGL10.EGL_NO_CONTEXT); 921 mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface); 922 mEglSurface = null; 923 } 924 } 925 926 public void finish() { 927 if (mEglContext != null) { 928 mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext); 929 mEglContext = null; 930 } 931 if (mEglDisplay != null) { 932 mEgl.eglTerminate(mEglDisplay); 933 mEglDisplay = null; 934 } 935 } 936 937 private void throwEglException(String function) { 938 throw new RuntimeException(function + " failed: " + mEgl.eglGetError()); 939 } 940 941 EGL10 mEgl; 942 EGLDisplay mEglDisplay; 943 EGLSurface mEglSurface; 944 EGLConfig mEglConfig; 945 EGLContext mEglContext; 946 } 947 948 /** 949 * A generic GL Thread. Takes care of initializing EGL and GL. Delegates 950 * to a Renderer instance to do the actual drawing. Can be configured to 951 * render continuously or on request. 952 * 953 * All potentially blocking synchronization is done through the 954 * sGLThreadManager object. This avoids multiple-lock ordering issues. 955 * 956 */ 957 class GLThread extends Thread { 958 GLThread(Renderer renderer) { 959 super(); 960 mWidth = 0; 961 mHeight = 0; 962 mRequestRender = true; 963 mRenderMode = RENDERMODE_CONTINUOUSLY; 964 mRenderer = renderer; 965 } 966 967 @Override 968 public void run() { 969 setName("GLThread " + getId()); 970 if (LOG_THREADS) { 971 Log.i("GLThread", "starting tid=" + getId()); 972 } 973 974 try { 975 guardedRun(); 976 } catch (InterruptedException e) { 977 // fall thru and exit normally 978 } finally { 979 sGLThreadManager.threadExiting(this); 980 } 981 } 982 983 /* 984 * This private method should only be called inside a 985 * synchronized(sGLThreadManager) block. 986 */ 987 private void stopEglLocked() { 988 if (mHaveEgl) { 989 mHaveEgl = false; 990 mEglHelper.destroySurface(); 991 mEglHelper.finish(); 992 sGLThreadManager.releaseEglSurfaceLocked(this); 993 } 994 } 995 996 private void guardedRun() throws InterruptedException { 997 mEglHelper = new EglHelper(); 998 try { 999 GL10 gl = null; 1000 boolean createEglSurface = false; 1001 boolean sizeChanged = false; 1002 int w = 0; 1003 int h = 0; 1004 Runnable event = null; 1005 1006 while (true) { 1007 synchronized (sGLThreadManager) { 1008 while (true) { 1009 if (mShouldExit) { 1010 return; 1011 } 1012 1013 if (! mEventQueue.isEmpty()) { 1014 event = mEventQueue.remove(0); 1015 break; 1016 } 1017 1018 // Do we need to release the EGL surface? 1019 if (mHaveEgl && mPaused) { 1020 if (LOG_SURFACE) { 1021 Log.i("GLThread", "releasing EGL surface because paused tid=" + getId()); 1022 } 1023 stopEglLocked(); 1024 } 1025 1026 // Have we lost the surface view surface? 1027 if ((! mHasSurface) && (! mWaitingForSurface)) { 1028 if (LOG_SURFACE) { 1029 Log.i("GLThread", "noticed surfaceView surface lost tid=" + getId()); 1030 } 1031 if (mHaveEgl) { 1032 stopEglLocked(); 1033 } 1034 mWaitingForSurface = true; 1035 sGLThreadManager.notifyAll(); 1036 } 1037 1038 // Have we acquired the surface view surface? 1039 if (mHasSurface && mWaitingForSurface) { 1040 if (LOG_SURFACE) { 1041 Log.i("GLThread", "noticed surfaceView surface acquired tid=" + getId()); 1042 } 1043 mWaitingForSurface = false; 1044 sGLThreadManager.notifyAll(); 1045 } 1046 1047 // Ready to draw? 1048 if ((!mPaused) && mHasSurface 1049 && (mWidth > 0) && (mHeight > 0) 1050 && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY))) { 1051 1052 // If we don't have an egl surface, try to acquire one. 1053 if ((! mHaveEgl) && sGLThreadManager.tryAcquireEglSurfaceLocked(this)) { 1054 mHaveEgl = true; 1055 mEglHelper.start(); 1056 createEglSurface = true; 1057 sizeChanged = true; 1058 sGLThreadManager.notifyAll(); 1059 } 1060 1061 if (mHaveEgl) { 1062 if (mSizeChanged) { 1063 sizeChanged = true; 1064 w = mWidth; 1065 h = mHeight; 1066 if (DRAW_TWICE_AFTER_SIZE_CHANGED) { 1067 // We keep mRequestRender true so that we draw twice after the size changes. 1068 // (Once because of mSizeChanged, the second time because of mRequestRender.) 1069 // This forces the updated graphics onto the screen. 1070 } else { 1071 mRequestRender = false; 1072 } 1073 mSizeChanged = false; 1074 } else { 1075 mRequestRender = false; 1076 } 1077 sGLThreadManager.notifyAll(); 1078 break; 1079 } 1080 } 1081 1082 // By design, this is the only place in a GLThread thread where we wait(). 1083 if (LOG_THREADS) { 1084 Log.i("GLThread", "waiting tid=" + getId()); 1085 } 1086 sGLThreadManager.wait(); 1087 } 1088 } // end of synchronized(sGLThreadManager) 1089 1090 if (event != null) { 1091 event.run(); 1092 event = null; 1093 continue; 1094 } 1095 1096 if (createEglSurface) { 1097 gl = (GL10) mEglHelper.createSurface(getHolder()); 1098 if (LOG_RENDERER) { 1099 Log.w("GLThread", "onSurfaceCreated"); 1100 } 1101 mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig); 1102 createEglSurface = false; 1103 } 1104 1105 if (sizeChanged) { 1106 if (LOG_RENDERER) { 1107 Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")"); 1108 } 1109 mRenderer.onSurfaceChanged(gl, w, h); 1110 sizeChanged = false; 1111 } 1112 1113 if (LOG_RENDERER) { 1114 Log.w("GLThread", "onDrawFrame"); 1115 } 1116 mRenderer.onDrawFrame(gl); 1117 if(!mEglHelper.swap()) { 1118 if (LOG_SURFACE) { 1119 Log.i("GLThread", "egl surface lost tid=" + getId()); 1120 } 1121 } 1122 } 1123 } finally { 1124 /* 1125 * clean-up everything... 1126 */ 1127 synchronized (sGLThreadManager) { 1128 stopEglLocked(); 1129 } 1130 } 1131 } 1132 1133 public void setRenderMode(int renderMode) { 1134 if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) { 1135 throw new IllegalArgumentException("renderMode"); 1136 } 1137 synchronized(sGLThreadManager) { 1138 mRenderMode = renderMode; 1139 sGLThreadManager.notifyAll(); 1140 } 1141 } 1142 1143 public int getRenderMode() { 1144 synchronized(sGLThreadManager) { 1145 return mRenderMode; 1146 } 1147 } 1148 1149 public void requestRender() { 1150 synchronized(sGLThreadManager) { 1151 mRequestRender = true; 1152 sGLThreadManager.notifyAll(); 1153 } 1154 } 1155 1156 public void surfaceCreated() { 1157 synchronized(sGLThreadManager) { 1158 if (LOG_THREADS) { 1159 Log.i("GLThread", "surfaceCreated tid=" + getId()); 1160 } 1161 mHasSurface = true; 1162 sGLThreadManager.notifyAll(); 1163 } 1164 } 1165 1166 public void surfaceDestroyed() { 1167 synchronized(sGLThreadManager) { 1168 if (LOG_THREADS) { 1169 Log.i("GLThread", "surfaceDestroyed tid=" + getId()); 1170 } 1171 mHasSurface = false; 1172 sGLThreadManager.notifyAll(); 1173 while((!mWaitingForSurface) && (!mExited)) { 1174 try { 1175 sGLThreadManager.wait(); 1176 } catch (InterruptedException e) { 1177 Thread.currentThread().interrupt(); 1178 } 1179 } 1180 } 1181 } 1182 1183 public void onPause() { 1184 synchronized (sGLThreadManager) { 1185 mPaused = true; 1186 sGLThreadManager.notifyAll(); 1187 } 1188 } 1189 1190 public void onResume() { 1191 synchronized (sGLThreadManager) { 1192 mPaused = false; 1193 mRequestRender = true; 1194 sGLThreadManager.notifyAll(); 1195 } 1196 } 1197 1198 public void onWindowResize(int w, int h) { 1199 synchronized (sGLThreadManager) { 1200 mWidth = w; 1201 mHeight = h; 1202 mSizeChanged = true; 1203 mRequestRender = true; 1204 sGLThreadManager.notifyAll(); 1205 } 1206 } 1207 1208 public void requestExitAndWait() { 1209 // don't call this from GLThread thread or it is a guaranteed 1210 // deadlock! 1211 synchronized(sGLThreadManager) { 1212 mShouldExit = true; 1213 sGLThreadManager.notifyAll(); 1214 while (! mExited) { 1215 try { 1216 sGLThreadManager.wait(); 1217 } catch (InterruptedException ex) { 1218 Thread.currentThread().interrupt(); 1219 } 1220 } 1221 } 1222 } 1223 1224 /** 1225 * Queue an "event" to be run on the GL rendering thread. 1226 * @param r the runnable to be run on the GL rendering thread. 1227 */ 1228 public void queueEvent(Runnable r) { 1229 if (r == null) { 1230 throw new IllegalArgumentException("r must not be null"); 1231 } 1232 synchronized(sGLThreadManager) { 1233 mEventQueue.add(r); 1234 sGLThreadManager.notifyAll(); 1235 } 1236 } 1237 1238 // Once the thread is started, all accesses to the following member 1239 // variables are protected by the sGLThreadManager monitor 1240 private boolean mShouldExit; 1241 private boolean mExited; 1242 private boolean mPaused; 1243 private boolean mHasSurface; 1244 private boolean mWaitingForSurface; 1245 private boolean mHaveEgl; 1246 private int mWidth; 1247 private int mHeight; 1248 private int mRenderMode; 1249 private boolean mRequestRender; 1250 private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>(); 1251 // End of member variables protected by the sGLThreadManager monitor. 1252 1253 private Renderer mRenderer; 1254 private EglHelper mEglHelper; 1255 } 1256 1257 static class LogWriter extends Writer { 1258 1259 @Override public void close() { 1260 flushBuilder(); 1261 } 1262 1263 @Override public void flush() { 1264 flushBuilder(); 1265 } 1266 1267 @Override public void write(char[] buf, int offset, int count) { 1268 for(int i = 0; i < count; i++) { 1269 char c = buf[offset + i]; 1270 if ( c == '\n') { 1271 flushBuilder(); 1272 } 1273 else { 1274 mBuilder.append(c); 1275 } 1276 } 1277 } 1278 1279 private void flushBuilder() { 1280 if (mBuilder.length() > 0) { 1281 Log.v("GLSurfaceView", mBuilder.toString()); 1282 mBuilder.delete(0, mBuilder.length()); 1283 } 1284 } 1285 1286 private StringBuilder mBuilder = new StringBuilder(); 1287 } 1288 1289 1290 private void checkRenderThreadState() { 1291 if (mGLThread != null) { 1292 throw new IllegalStateException( 1293 "setRenderer has already been called for this instance."); 1294 } 1295 } 1296 1297 private static class GLThreadManager { 1298 1299 public synchronized void threadExiting(GLThread thread) { 1300 if (LOG_THREADS) { 1301 Log.i("GLThread", "exiting tid=" + thread.getId()); 1302 } 1303 thread.mExited = true; 1304 if (mEglOwner == thread) { 1305 mEglOwner = null; 1306 } 1307 notifyAll(); 1308 } 1309 1310 /* 1311 * Tries once to acquire the right to use an EGL 1312 * surface. Does not block. Requires that we are already 1313 * in the sGLThreadManager monitor when this is called. 1314 * @return true if the right to use an EGL surface was acquired. 1315 */ 1316 public boolean tryAcquireEglSurfaceLocked(GLThread thread) { 1317 if (mEglOwner == thread || mEglOwner == null) { 1318 mEglOwner = thread; 1319 notifyAll(); 1320 return true; 1321 } 1322 return false; 1323 } 1324 /* 1325 * Releases the EGL surface. Requires that we are already in the 1326 * sGLThreadManager monitor when this is called. 1327 */ 1328 public void releaseEglSurfaceLocked(GLThread thread) { 1329 if (mEglOwner == thread) { 1330 mEglOwner = null; 1331 } 1332 notifyAll(); 1333 } 1334 1335 private GLThread mEglOwner; 1336 } 1337 1338 private static final GLThreadManager sGLThreadManager = new GLThreadManager(); 1339 private boolean mSizeChanged = true; 1340 1341 private GLThread mGLThread; 1342 private EGLConfigChooser mEGLConfigChooser; 1343 private EGLContextFactory mEGLContextFactory; 1344 private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory; 1345 private GLWrapper mGLWrapper; 1346 private int mDebugFlags; 1347} 1348