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