GLSurfaceView.java revision 1bd888ba2e0976a179258cfa65ef07be31602a0a
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 d = findConfigAttrib(egl, display, config, 659 EGL10.EGL_DEPTH_SIZE, 0); 660 int s = findConfigAttrib(egl, display, config, 661 EGL10.EGL_STENCIL_SIZE, 0); 662 if (d >= mDepthSize && s>= mStencilSize) { 663 int r = findConfigAttrib(egl, display, config, 664 EGL10.EGL_RED_SIZE, 0); 665 int g = findConfigAttrib(egl, display, config, 666 EGL10.EGL_GREEN_SIZE, 0); 667 int b = findConfigAttrib(egl, display, config, 668 EGL10.EGL_BLUE_SIZE, 0); 669 int a = findConfigAttrib(egl, display, config, 670 EGL10.EGL_ALPHA_SIZE, 0); 671 int distance = Math.abs(r - mRedSize) 672 + Math.abs(g - mGreenSize) 673 + Math.abs(b - mBlueSize) 674 + Math.abs(a - mAlphaSize); 675 if (distance < closestDistance) { 676 closestDistance = distance; 677 closestConfig = config; 678 } 679 } 680 } 681 return closestConfig; 682 } 683 684 private int findConfigAttrib(EGL10 egl, EGLDisplay display, 685 EGLConfig config, int attribute, int defaultValue) { 686 687 if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) { 688 return mValue[0]; 689 } 690 return defaultValue; 691 } 692 693 private int[] mValue; 694 // Subclasses can adjust these values: 695 protected int mRedSize; 696 protected int mGreenSize; 697 protected int mBlueSize; 698 protected int mAlphaSize; 699 protected int mDepthSize; 700 protected int mStencilSize; 701 } 702 703 /** 704 * This class will choose a supported surface as close to 705 * RGB565 as possible, with or without a depth buffer. 706 * 707 */ 708 private static class SimpleEGLConfigChooser extends ComponentSizeChooser { 709 public SimpleEGLConfigChooser(boolean withDepthBuffer) { 710 super(4, 4, 4, 0, withDepthBuffer ? 16 : 0, 0); 711 // Adjust target values. This way we'll accept a 4444 or 712 // 555 buffer if there's no 565 buffer available. 713 mRedSize = 5; 714 mGreenSize = 6; 715 mBlueSize = 5; 716 } 717 } 718 719 /** 720 * An EGL helper class. 721 */ 722 723 private class EglHelper { 724 public EglHelper() { 725 726 } 727 728 /** 729 * Initialize EGL for a given configuration spec. 730 * @param configSpec 731 */ 732 public void start(){ 733 /* 734 * Get an EGL instance 735 */ 736 mEgl = (EGL10) EGLContext.getEGL(); 737 738 /* 739 * Get to the default display. 740 */ 741 mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); 742 743 /* 744 * We can now initialize EGL for that display 745 */ 746 int[] version = new int[2]; 747 mEgl.eglInitialize(mEglDisplay, version); 748 mEglConfig = mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay); 749 750 /* 751 * Create an OpenGL ES context. This must be done only once, an 752 * OpenGL context is a somewhat heavy object. 753 */ 754 mEglContext = mEgl.eglCreateContext(mEglDisplay, mEglConfig, 755 EGL10.EGL_NO_CONTEXT, null); 756 757 mEglSurface = null; 758 } 759 760 /* 761 * React to the creation of a new surface by creating and returning an 762 * OpenGL interface that renders to that surface. 763 */ 764 public GL createSurface(SurfaceHolder holder) { 765 /* 766 * The window size has changed, so we need to create a new 767 * surface. 768 */ 769 if (mEglSurface != null) { 770 771 /* 772 * Unbind and destroy the old EGL surface, if 773 * there is one. 774 */ 775 mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, 776 EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); 777 mEgl.eglDestroySurface(mEglDisplay, mEglSurface); 778 } 779 780 /* 781 * Create an EGL surface we can render into. 782 */ 783 mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, 784 mEglConfig, holder, null); 785 786 /* 787 * Before we can issue GL commands, we need to make sure 788 * the context is current and bound to a surface. 789 */ 790 mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, 791 mEglContext); 792 793 794 GL gl = mEglContext.getGL(); 795 if (mGLWrapper != null) { 796 gl = mGLWrapper.wrap(gl); 797 } 798 799 if ((mDebugFlags & (DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS))!= 0) { 800 int configFlags = 0; 801 Writer log = null; 802 if ((mDebugFlags & DEBUG_CHECK_GL_ERROR) != 0) { 803 configFlags |= GLDebugHelper.CONFIG_CHECK_GL_ERROR; 804 } 805 if ((mDebugFlags & DEBUG_LOG_GL_CALLS) != 0) { 806 log = new LogWriter(); 807 } 808 gl = GLDebugHelper.wrap(gl, configFlags, log); 809 } 810 return gl; 811 } 812 813 /** 814 * Display the current render surface. 815 * @return false if the context has been lost. 816 */ 817 public boolean swap() { 818 mEgl.eglSwapBuffers(mEglDisplay, mEglSurface); 819 820 /* 821 * Always check for EGL_CONTEXT_LOST, which means the context 822 * and all associated data were lost (For instance because 823 * the device went to sleep). We need to sleep until we 824 * get a new surface. 825 */ 826 return mEgl.eglGetError() != EGL11.EGL_CONTEXT_LOST; 827 } 828 829 public void destroySurface() { 830 if (mEglSurface != null) { 831 mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, 832 EGL10.EGL_NO_SURFACE, 833 EGL10.EGL_NO_CONTEXT); 834 mEgl.eglDestroySurface(mEglDisplay, mEglSurface); 835 mEglSurface = null; 836 } 837 } 838 839 public void finish() { 840 if (mEglContext != null) { 841 mEgl.eglDestroyContext(mEglDisplay, mEglContext); 842 mEglContext = null; 843 } 844 if (mEglDisplay != null) { 845 mEgl.eglTerminate(mEglDisplay); 846 mEglDisplay = null; 847 } 848 } 849 850 EGL10 mEgl; 851 EGLDisplay mEglDisplay; 852 EGLSurface mEglSurface; 853 EGLConfig mEglConfig; 854 EGLContext mEglContext; 855 } 856 857 /** 858 * A generic GL Thread. Takes care of initializing EGL and GL. Delegates 859 * to a Renderer instance to do the actual drawing. Can be configured to 860 * render continuously or on request. 861 * 862 */ 863 class GLThread extends Thread { 864 GLThread(Renderer renderer) { 865 super(); 866 mDone = false; 867 mWidth = 0; 868 mHeight = 0; 869 mRequestRender = true; 870 mRenderMode = RENDERMODE_CONTINUOUSLY; 871 mRenderer = renderer; 872 setName("GLThread"); 873 } 874 875 @Override 876 public void run() { 877 /* 878 * When the android framework launches a second instance of 879 * an activity, the new instance's onCreate() method may be 880 * called before the first instance returns from onDestroy(). 881 * 882 * This semaphore ensures that only one instance at a time 883 * accesses EGL. 884 */ 885 try { 886 try { 887 sEglSemaphore.acquire(); 888 } catch (InterruptedException e) { 889 return; 890 } 891 guardedRun(); 892 } catch (InterruptedException e) { 893 // fall thru and exit normally 894 } finally { 895 sEglSemaphore.release(); 896 } 897 } 898 899 private void guardedRun() throws InterruptedException { 900 mEglHelper = new EglHelper(); 901 mEglHelper.start(); 902 903 GL10 gl = null; 904 boolean tellRendererSurfaceCreated = true; 905 boolean tellRendererSurfaceChanged = true; 906 907 /* 908 * This is our main activity thread's loop, we go until 909 * asked to quit. 910 */ 911 while (!mDone) { 912 913 /* 914 * Update the asynchronous state (window size) 915 */ 916 int w, h; 917 boolean changed; 918 boolean needStart = false; 919 synchronized (this) { 920 Runnable r; 921 while ((r = getEvent()) != null) { 922 r.run(); 923 } 924 if (mPaused) { 925 mEglHelper.destroySurface(); 926 mEglHelper.finish(); 927 needStart = true; 928 } 929 while (needToWait()) { 930 if (!mHasSurface) { 931 if (!mWaitingForSurface) { 932 mEglHelper.destroySurface(); 933 mWaitingForSurface = true; 934 notify(); 935 } 936 } 937 wait(); 938 } 939 if (mDone) { 940 break; 941 } 942 changed = mSizeChanged; 943 w = mWidth; 944 h = mHeight; 945 mSizeChanged = false; 946 mRequestRender = false; 947 if (mHasSurface && mWaitingForSurface) { 948 changed = true; 949 mWaitingForSurface = false; 950 mRequestRender = true; // Forces a redraw for RENDERMODE_RENDER_WHEN_DIRTY 951 } 952 } 953 if (needStart) { 954 mEglHelper.start(); 955 tellRendererSurfaceCreated = true; 956 changed = true; 957 } 958 if (changed) { 959 gl = (GL10) mEglHelper.createSurface(getHolder()); 960 tellRendererSurfaceChanged = true; 961 } 962 if (tellRendererSurfaceCreated) { 963 mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig); 964 tellRendererSurfaceCreated = false; 965 } 966 if (tellRendererSurfaceChanged) { 967 mRenderer.onSurfaceChanged(gl, w, h); 968 tellRendererSurfaceChanged = false; 969 } 970 if ((w > 0) && (h > 0)) { 971 /* draw a frame here */ 972 mRenderer.onDrawFrame(gl); 973 974 /* 975 * Once we're done with GL, we need to call swapBuffers() 976 * to instruct the system to display the rendered frame 977 */ 978 mEglHelper.swap(); 979 } 980 } 981 982 /* 983 * clean-up everything... 984 */ 985 mEglHelper.destroySurface(); 986 mEglHelper.finish(); 987 } 988 989 private boolean needToWait() { 990 if (mDone) { 991 return false; 992 } 993 994 if (mPaused || (! mHasSurface)) { 995 return true; 996 } 997 998 if ((mWidth > 0) && (mHeight > 0) && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY))) { 999 return false; 1000 } 1001 1002 return true; 1003 } 1004 1005 public void setRenderMode(int renderMode) { 1006 if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) { 1007 throw new IllegalArgumentException("renderMode"); 1008 } 1009 synchronized(this) { 1010 mRenderMode = renderMode; 1011 if (renderMode == RENDERMODE_CONTINUOUSLY) { 1012 notify(); 1013 } 1014 } 1015 } 1016 1017 public int getRenderMode() { 1018 synchronized(this) { 1019 return mRenderMode; 1020 } 1021 } 1022 1023 public void requestRender() { 1024 synchronized(this) { 1025 mRequestRender = true; 1026 notify(); 1027 } 1028 } 1029 1030 public void surfaceCreated() { 1031 synchronized(this) { 1032 mHasSurface = true; 1033 notify(); 1034 } 1035 } 1036 1037 public void surfaceDestroyed() { 1038 synchronized(this) { 1039 mHasSurface = false; 1040 notify(); 1041 while(!mWaitingForSurface) { 1042 try { 1043 wait(); 1044 } catch (InterruptedException e) { 1045 Thread.currentThread().interrupt(); 1046 } 1047 } 1048 } 1049 } 1050 1051 public void onPause() { 1052 synchronized (this) { 1053 mPaused = true; 1054 } 1055 } 1056 1057 public void onResume() { 1058 synchronized (this) { 1059 mPaused = false; 1060 notify(); 1061 } 1062 } 1063 1064 public void onWindowResize(int w, int h) { 1065 synchronized (this) { 1066 mWidth = w; 1067 mHeight = h; 1068 mSizeChanged = true; 1069 notify(); 1070 } 1071 } 1072 1073 public void requestExitAndWait() { 1074 // don't call this from GLThread thread or it is a guaranteed 1075 // deadlock! 1076 synchronized(this) { 1077 mDone = true; 1078 notify(); 1079 } 1080 try { 1081 join(); 1082 } catch (InterruptedException ex) { 1083 Thread.currentThread().interrupt(); 1084 } 1085 } 1086 1087 /** 1088 * Queue an "event" to be run on the GL rendering thread. 1089 * @param r the runnable to be run on the GL rendering thread. 1090 */ 1091 public void queueEvent(Runnable r) { 1092 synchronized(this) { 1093 mEventQueue.add(r); 1094 } 1095 } 1096 1097 private Runnable getEvent() { 1098 synchronized(this) { 1099 if (mEventQueue.size() > 0) { 1100 return mEventQueue.remove(0); 1101 } 1102 1103 } 1104 return null; 1105 } 1106 1107 private boolean mDone; 1108 private boolean mPaused; 1109 private boolean mHasSurface; 1110 private boolean mWaitingForSurface; 1111 private int mWidth; 1112 private int mHeight; 1113 private int mRenderMode; 1114 private boolean mRequestRender; 1115 private Renderer mRenderer; 1116 private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>(); 1117 private EglHelper mEglHelper; 1118 } 1119 1120 static class LogWriter extends Writer { 1121 1122 @Override public void close() { 1123 flushBuilder(); 1124 } 1125 1126 @Override public void flush() { 1127 flushBuilder(); 1128 } 1129 1130 @Override public void write(char[] buf, int offset, int count) { 1131 for(int i = 0; i < count; i++) { 1132 char c = buf[offset + i]; 1133 if ( c == '\n') { 1134 flushBuilder(); 1135 } 1136 else { 1137 mBuilder.append(c); 1138 } 1139 } 1140 } 1141 1142 private void flushBuilder() { 1143 if (mBuilder.length() > 0) { 1144 Log.v("GLSurfaceView", mBuilder.toString()); 1145 mBuilder.delete(0, mBuilder.length()); 1146 } 1147 } 1148 1149 private StringBuilder mBuilder = new StringBuilder(); 1150 } 1151 1152 private static final Semaphore sEglSemaphore = new Semaphore(1); 1153 private boolean mSizeChanged = true; 1154 1155 private GLThread mGLThread; 1156 private EGLConfigChooser mEGLConfigChooser; 1157 private GLWrapper mGLWrapper; 1158 private int mDebugFlags; 1159} 1160