GLSurfaceView.java revision a605a51e63de2e7dc99ed085639a6f12b7f85af7
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.IOException; 20import java.io.Writer; 21import java.util.ArrayList; 22import java.util.concurrent.Semaphore; 23 24import javax.microedition.khronos.egl.EGL10; 25import javax.microedition.khronos.egl.EGL11; 26import javax.microedition.khronos.egl.EGLConfig; 27import javax.microedition.khronos.egl.EGLContext; 28import javax.microedition.khronos.egl.EGLDisplay; 29import javax.microedition.khronos.egl.EGLSurface; 30import javax.microedition.khronos.opengles.GL; 31import javax.microedition.khronos.opengles.GL10; 32 33import android.content.Context; 34import android.util.AttributeSet; 35import android.util.Log; 36import android.view.SurfaceHolder; 37import android.view.SurfaceView; 38 39/** 40 * An implementation of SurfaceView that uses the dedicated surface for 41 * displaying an OpenGL animation. This allows the animation to run in a 42 * separate thread, without requiring that it be driven by the update mechanism 43 * of the view hierarchy. 44 * 45 * The application-specific rendering code is delegated to a GLView.Renderer 46 * instance. 47 */ 48public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback { 49 public final static int RENDERMODE_WHEN_DIRTY = 0; 50 public final static int RENDERMODE_CONTUOUSLY = 1; 51 52 /** 53 * Check glError() after every GL call. 54 */ 55 public final static int DEBUG_CHECK_GL_ERROR = 1; 56 57 /** 58 * Log GL calls to the system log at "verbose" level with tag "GLSurfaceView". 59 */ 60 public final static int DEBUG_LOG_GL_CALLS = 2; 61 62 public GLSurfaceView(Context context) { 63 super(context); 64 init(); 65 } 66 67 public GLSurfaceView(Context context, AttributeSet attrs) { 68 super(context, attrs); 69 init(); 70 } 71 72 private void init() { 73 // Install a SurfaceHolder.Callback so we get notified when the 74 // underlying surface is created and destroyed 75 SurfaceHolder holder = getHolder(); 76 holder.addCallback(this); 77 holder.setType(SurfaceHolder.SURFACE_TYPE_GPU); 78 } 79 80 /** 81 * Set the glWrapper to a new value. The current glWrapper is used 82 * whenever a surface is created. The default value is null. 83 * @param glWrapper the new GLWrapper 84 */ 85 public void setGLWrapper(GLWrapper glWrapper) { 86 mGLWrapper = glWrapper; 87 } 88 89 /** 90 * Set the debug flags to a new value. The debug flags take effect 91 * whenever a surface is created. The default value is zero. 92 * @param debugFlags the new debug flags 93 * @see #DEBUG_CHECK_GL_ERROR 94 * @see #DEBUG_LOG_GL_CALLS 95 */ 96 public void setDebugFlags(int debugFlags) { 97 mDebugFlags = debugFlags; 98 } 99 100 public int getDebugFlags() { 101 return mDebugFlags; 102 } 103 104 /** 105 * Set the renderer associated with this view. Can only be called once. 106 * @param renderer 107 */ 108 public void setRenderer(Renderer renderer) { 109 if (mGLThread != null) { 110 throw new IllegalStateException( 111 "setRenderer has already been called for this instance."); 112 } 113 if (mEGLConfigChooser == null) { 114 mEGLConfigChooser = new SimpleEGLConfigChooser(true); 115 } 116 mGLThread = new GLThread(renderer); 117 mGLThread.start(); 118 } 119 120 /** 121 * Set the EGLConfigChooser associated with this view. If this method is 122 * called at all, it must be called before {@link #setRenderer(Renderer)} 123 * is called. 124 * <p> 125 * The supplied configChooser will be used to choose a configuration. 126 * @param configChooser 127 */ 128 public void setEGLConfigChooser(EGLConfigChooser configChooser) { 129 if (mGLThread != null) { 130 throw new IllegalStateException( 131 "setRenderer has already been called for this instance."); 132 } 133 mEGLConfigChooser = configChooser; 134 } 135 136 /** 137 * Set the EGLConfigChooser associated with this view. If this method is 138 * called, it must be called before {@link #setRenderer(Renderer)} 139 * is called. 140 * <p> 141 * This method installs a config chooser which will choose a config 142 * as close to 16-bit RGB as possible, with or without an optional depth 143 * buffer as close to 16-bits as possible. 144 * <p> 145 * If no setEGLConfigChooser method is called, then by default the 146 * view will choose a config as close to 16-bit RGB as possible, with 147 * a depth buffer as close to 16-bits as possible. 148 * 149 * @param needDepth 150 */ 151 public void setEGLConfigChooser(boolean needDepth) { 152 setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth)); 153 } 154 155 /** 156 * Set the EGLConfigChooser associated with this view. If this method is 157 * called, it must be called before {@link #setRenderer(Renderer)} 158 * is called. 159 * <p> 160 * This method installs a config chooser which will choose a config 161 * with at least the specified component sizes, and as close 162 * to the specified component sizes as possible. 163 * 164 */ 165 public void setEGLConfigChooser(int redSize, int greenSize, int blueSize, 166 int alphaSize, int depthSize, int stencilSize) { 167 setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize, 168 blueSize, alphaSize, depthSize, stencilSize)); 169 } 170 /** 171 * Set the rendering mode. When the renderMode is 172 * RENDERMODE_CONTINUOUSLY, the renderer is called 173 * repeatedly to re-render the scene. When the rendermode 174 * is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface 175 * is created, or when requestRender is called. Defaults to RENDERMODE_CONTINUOUSLY. 176 * @param renderMode one of the RENDERMODE_X constants 177 */ 178 public void setRenderMode(int renderMode) { 179 mGLThread.setRenderMode(renderMode); 180 } 181 182 /** 183 * Get the current rendering mode. May be called 184 * from any thread. Must not be called before a renderer has been set. 185 * @return true if the renderer will render continuously. 186 */ 187 public int getRenderMode() { 188 return mGLThread.getRenderMode(); 189 } 190 191 /** 192 * Request that the renderer render a frame. May be called 193 * from any thread. Must not be called before a renderer has been set. 194 * This method is typically used when the render mode has been set to 195 * false, so that frames are only rendered on demand. 196 */ 197 public void requestRender() { 198 mGLThread.requestRender(); 199 } 200 201 public void surfaceCreated(SurfaceHolder holder) { 202 mGLThread.surfaceCreated(); 203 } 204 205 public void surfaceDestroyed(SurfaceHolder holder) { 206 // Surface will be destroyed when we return 207 mGLThread.surfaceDestroyed(); 208 } 209 210 public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { 211 mGLThread.onWindowResize(w, h); 212 } 213 214 /** 215 * Inform the view that the activity is paused. The owner of this view must 216 * call this method when the activity is paused. 217 */ 218 public void onPause() { 219 mGLThread.onPause(); 220 } 221 222 /** 223 * Inform the view that the activity is resumed. The owner of this view must 224 * call this method when the activity is resumed. 225 */ 226 public void onResume() { 227 mGLThread.onResume(); 228 } 229 230 /** 231 * Queue an "event" to be run on the GL rendering thread. 232 * @param r the runnable to be run on the GL rendering thread. 233 */ 234 public void queueEvent(Runnable r) { 235 mGLThread.queueEvent(r); 236 } 237 238 @Override 239 protected void onDetachedFromWindow() { 240 super.onDetachedFromWindow(); 241 mGLThread.requestExitAndWait(); 242 } 243 244 // ---------------------------------------------------------------------- 245 246 public interface GLWrapper { 247 GL wrap(GL gl); 248 } 249 250 // ---------------------------------------------------------------------- 251 252 /** 253 * A generic renderer interface. 254 */ 255 public interface Renderer { 256 /** 257 * Surface created. 258 * Called when the surface is created. Called when the application 259 * starts, and whenever the GPU is reinitialized. This will 260 * typically happen when the device awakes after going to sleep. 261 * Set your textures here. 262 * @param gl the GL interface. Use <code>instanceof</code> to 263 * test if the interface supports GL11 or higher interfaces. 264 * @param config the EGLConfig of the created surface. Can be used 265 * to create matching pbuffers. 266 */ 267 void onSurfaceCreated(GL10 gl, EGLConfig config); 268 /** 269 * Surface changed size. 270 * Called after the surface is created and whenever 271 * the OpenGL ES surface size changes. Set your viewport here. 272 * @param gl the GL interface. Use <code>instanceof</code> to 273 * test if the interface supports GL11 or higher interfaces. 274 * @param width 275 * @param height 276 */ 277 void onSurfaceChanged(GL10 gl, int width, int height); 278 /** 279 * Draw the current frame. 280 * @param gl the GL interface. Use <code>instanceof</code> to 281 * test if the interface supports GL11 or higher interfaces. 282 */ 283 void onDrawFrame(GL10 gl); 284 } 285 286 /** 287 * An interface for choosing a configuration from a list of 288 * potential configurations. 289 * 290 */ 291 public interface EGLConfigChooser { 292 /** 293 * Choose a configuration from the list. Implementors typically 294 * implement this method by calling 295 * {@link EGL10#eglChooseConfig} and iterating through the results. 296 * @param egl the EGL10 for the current display. 297 * @param display the current display. 298 * @return the chosen configuration. 299 */ 300 EGLConfig chooseConfig(EGL10 egl, EGLDisplay display); 301 } 302 303 private static abstract class BaseConfigChooser 304 implements EGLConfigChooser { 305 public BaseConfigChooser(int[] configSpec) { 306 mConfigSpec = configSpec; 307 } 308 public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { 309 int[] num_config = new int[1]; 310 egl.eglChooseConfig(display, mConfigSpec, null, 0, num_config); 311 312 int numConfigs = num_config[0]; 313 314 if (numConfigs <= 0) { 315 throw new IllegalArgumentException( 316 "No configs match configSpec"); 317 } 318 319 EGLConfig[] configs = new EGLConfig[numConfigs]; 320 egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, 321 num_config); 322 EGLConfig config = chooseConfig(egl, display, configs); 323 if (config == null) { 324 throw new IllegalArgumentException("No config chosen"); 325 } 326 return config; 327 } 328 329 abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, 330 EGLConfig[] configs); 331 332 protected int[] mConfigSpec; 333 } 334 335 private static class ComponentSizeChooser extends BaseConfigChooser { 336 public ComponentSizeChooser(int redSize, int greenSize, int blueSize, 337 int alphaSize, int depthSize, int stencilSize) { 338 super(new int[] { 339 EGL10.EGL_RED_SIZE, redSize, 340 EGL10.EGL_GREEN_SIZE, greenSize, 341 EGL10.EGL_BLUE_SIZE, blueSize, 342 EGL10.EGL_ALPHA_SIZE, alphaSize, 343 EGL10.EGL_DEPTH_SIZE, depthSize, 344 EGL10.EGL_STENCIL_SIZE, stencilSize, 345 EGL10.EGL_NONE}); 346 mValue = new int[1]; 347 mRedSize = redSize; 348 mGreenSize = greenSize; 349 mBlueSize = blueSize; 350 mAlphaSize = alphaSize; 351 mDepthSize = depthSize; 352 mStencilSize = stencilSize; 353 } 354 355 @Override 356 public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, 357 EGLConfig[] configs) { 358 EGLConfig closestConfig = null; 359 int closestDistance = 1000; 360 for(EGLConfig config : configs) { 361 int r = findConfigAttrib(egl, display, config, 362 EGL10.EGL_RED_SIZE, 0); 363 int g = findConfigAttrib(egl, display, config, 364 EGL10.EGL_GREEN_SIZE, 0); 365 int b = findConfigAttrib(egl, display, config, 366 EGL10.EGL_BLUE_SIZE, 0); 367 int a = findConfigAttrib(egl, display, config, 368 EGL10.EGL_ALPHA_SIZE, 0); 369 int d = findConfigAttrib(egl, display, config, 370 EGL10.EGL_DEPTH_SIZE, 0); 371 int s = findConfigAttrib(egl, display, config, 372 EGL10.EGL_STENCIL_SIZE, 0); 373 int distance = Math.abs(r - mRedSize) 374 + Math.abs(g - mGreenSize) 375 + Math.abs(b - mBlueSize) + Math.abs(a - mAlphaSize) 376 + Math.abs(d - mDepthSize) + Math.abs(s - mStencilSize); 377 if (distance < closestDistance) { 378 closestDistance = distance; 379 closestConfig = config; 380 } 381 } 382 return closestConfig; 383 } 384 385 private int findConfigAttrib(EGL10 egl, EGLDisplay display, 386 EGLConfig config, int attribute, int defaultValue) { 387 388 if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) { 389 return mValue[0]; 390 } 391 return defaultValue; 392 } 393 394 private int[] mValue; 395 // Subclasses can adjust these values: 396 protected int mRedSize; 397 protected int mGreenSize; 398 protected int mBlueSize; 399 protected int mAlphaSize; 400 protected int mDepthSize; 401 protected int mStencilSize; 402 } 403 404 /** 405 * This class will choose a supported surface as close to 406 * RGB565 as possible, with or without a depth buffer. 407 * 408 */ 409 private static class SimpleEGLConfigChooser extends ComponentSizeChooser { 410 public SimpleEGLConfigChooser(boolean withDepthBuffer) { 411 super(4, 4, 4, 0, withDepthBuffer ? 16 : 0, 0); 412 // Adjust target values. This way we'll accept a 4444 or 413 // 555 buffer if there's no 565 buffer available. 414 mRedSize = 5; 415 mGreenSize = 6; 416 mBlueSize = 5; 417 } 418 } 419 420 /** 421 * An EGL helper class. 422 */ 423 424 private class EglHelper { 425 public EglHelper() { 426 427 } 428 429 /** 430 * Initialize EGL for a given configuration spec. 431 * @param configSpec 432 */ 433 public void start(){ 434 /* 435 * Get an EGL instance 436 */ 437 mEgl = (EGL10) EGLContext.getEGL(); 438 439 /* 440 * Get to the default display. 441 */ 442 mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); 443 444 /* 445 * We can now initialize EGL for that display 446 */ 447 int[] version = new int[2]; 448 mEgl.eglInitialize(mEglDisplay, version); 449 mEglConfig = mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay); 450 451 /* 452 * Create an OpenGL ES context. This must be done only once, an 453 * OpenGL context is a somewhat heavy object. 454 */ 455 mEglContext = mEgl.eglCreateContext(mEglDisplay, mEglConfig, 456 EGL10.EGL_NO_CONTEXT, null); 457 458 mEglSurface = null; 459 } 460 461 /* 462 * React to the creation of a new surface by creating and returning an 463 * OpenGL interface that renders to that surface. 464 */ 465 public GL createSurface(SurfaceHolder holder) { 466 /* 467 * The window size has changed, so we need to create a new 468 * surface. 469 */ 470 if (mEglSurface != null) { 471 472 /* 473 * Unbind and destroy the old EGL surface, if 474 * there is one. 475 */ 476 mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, 477 EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); 478 mEgl.eglDestroySurface(mEglDisplay, mEglSurface); 479 } 480 481 /* 482 * Create an EGL surface we can render into. 483 */ 484 mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, 485 mEglConfig, holder, null); 486 487 /* 488 * Before we can issue GL commands, we need to make sure 489 * the context is current and bound to a surface. 490 */ 491 mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, 492 mEglContext); 493 494 495 GL gl = mEglContext.getGL(); 496 if (mGLWrapper != null) { 497 gl = mGLWrapper.wrap(gl); 498 } 499 500 if ((mDebugFlags & (DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS))!= 0) { 501 int configFlags = 0; 502 Writer log = null; 503 if ((mDebugFlags & DEBUG_CHECK_GL_ERROR) != 0) { 504 configFlags |= GLDebugHelper.CONFIG_CHECK_GL_ERROR; 505 } 506 if ((mDebugFlags & DEBUG_LOG_GL_CALLS) != 0) { 507 log = new LogWriter(); 508 } 509 gl = GLDebugHelper.wrap(gl, configFlags, log); 510 } 511 return gl; 512 } 513 514 /** 515 * Display the current render surface. 516 * @return false if the context has been lost. 517 */ 518 public boolean swap() { 519 mEgl.eglSwapBuffers(mEglDisplay, mEglSurface); 520 521 /* 522 * Always check for EGL_CONTEXT_LOST, which means the context 523 * and all associated data were lost (For instance because 524 * the device went to sleep). We need to sleep until we 525 * get a new surface. 526 */ 527 return mEgl.eglGetError() != EGL11.EGL_CONTEXT_LOST; 528 } 529 530 public void finish() { 531 if (mEglSurface != null) { 532 mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, 533 EGL10.EGL_NO_SURFACE, 534 EGL10.EGL_NO_CONTEXT); 535 mEgl.eglDestroySurface(mEglDisplay, mEglSurface); 536 mEglSurface = null; 537 } 538 if (mEglContext != null) { 539 mEgl.eglDestroyContext(mEglDisplay, mEglContext); 540 mEglContext = null; 541 } 542 if (mEglDisplay != null) { 543 mEgl.eglTerminate(mEglDisplay); 544 mEglDisplay = null; 545 } 546 } 547 548 EGL10 mEgl; 549 EGLDisplay mEglDisplay; 550 EGLSurface mEglSurface; 551 EGLConfig mEglConfig; 552 EGLContext mEglContext; 553 } 554 555 /** 556 * A generic GL Thread. Takes care of initializing EGL and GL. Delegates 557 * to a Renderer instance to do the actual drawing. Can be configured to 558 * render continuously or on request. 559 * 560 */ 561 class GLThread extends Thread { 562 GLThread(Renderer renderer) { 563 super(); 564 mDone = false; 565 mWidth = 0; 566 mHeight = 0; 567 mRequestRender = true; 568 mRenderMode = RENDERMODE_CONTUOUSLY; 569 mRenderer = renderer; 570 setName("GLThread"); 571 } 572 573 @Override 574 public void run() { 575 /* 576 * When the android framework launches a second instance of 577 * an activity, the new instance's onCreate() method may be 578 * called before the first instance returns from onDestroy(). 579 * 580 * This semaphore ensures that only one instance at a time 581 * accesses EGL. 582 */ 583 try { 584 try { 585 sEglSemaphore.acquire(); 586 } catch (InterruptedException e) { 587 return; 588 } 589 guardedRun(); 590 } catch (InterruptedException e) { 591 // fall thru and exit normally 592 } finally { 593 sEglSemaphore.release(); 594 } 595 } 596 597 private void guardedRun() throws InterruptedException { 598 mEglHelper = new EglHelper(); 599 mEglHelper.start(); 600 601 GL10 gl = null; 602 boolean tellRendererSurfaceCreated = true; 603 boolean tellRendererSurfaceChanged = true; 604 605 /* 606 * This is our main activity thread's loop, we go until 607 * asked to quit. 608 */ 609 while (!mDone) { 610 611 /* 612 * Update the asynchronous state (window size) 613 */ 614 int w, h; 615 boolean changed; 616 boolean needStart = false; 617 synchronized (this) { 618 Runnable r; 619 while ((r = getEvent()) != null) { 620 r.run(); 621 } 622 if (mPaused) { 623 mEglHelper.finish(); 624 needStart = true; 625 } 626 while (needToWait()) { 627 wait(); 628 } 629 if (mDone) { 630 break; 631 } 632 changed = mSizeChanged; 633 w = mWidth; 634 h = mHeight; 635 mSizeChanged = false; 636 mRequestRender = false; 637 } 638 if (needStart) { 639 mEglHelper.start(); 640 tellRendererSurfaceCreated = true; 641 changed = true; 642 } 643 if (changed) { 644 gl = (GL10) mEglHelper.createSurface(getHolder()); 645 tellRendererSurfaceChanged = true; 646 } 647 if (tellRendererSurfaceCreated) { 648 mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig); 649 tellRendererSurfaceCreated = false; 650 } 651 if (tellRendererSurfaceChanged) { 652 mRenderer.onSurfaceChanged(gl, w, h); 653 tellRendererSurfaceChanged = false; 654 } 655 if ((w > 0) && (h > 0)) { 656 /* draw a frame here */ 657 mRenderer.onDrawFrame(gl); 658 659 /* 660 * Once we're done with GL, we need to call swapBuffers() 661 * to instruct the system to display the rendered frame 662 */ 663 mEglHelper.swap(); 664 } 665 } 666 667 /* 668 * clean-up everything... 669 */ 670 mEglHelper.finish(); 671 } 672 673 private boolean needToWait() { 674 if (mDone) { 675 return false; 676 } 677 678 if (mPaused || (! mHasSurface)) { 679 return true; 680 } 681 682 if ((mWidth > 0) && (mHeight > 0) && (mRequestRender || (mRenderMode == RENDERMODE_CONTUOUSLY))) { 683 return false; 684 } 685 686 return true; 687 } 688 689 public void setRenderMode(int renderMode) { 690 if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTUOUSLY)) ) { 691 throw new IllegalArgumentException("renderMode"); 692 } 693 synchronized(this) { 694 mRenderMode = renderMode; 695 if (renderMode == RENDERMODE_CONTUOUSLY) { 696 notify(); 697 } 698 } 699 } 700 701 public int getRenderMode() { 702 synchronized(this) { 703 return mRenderMode; 704 } 705 } 706 707 public void requestRender() { 708 synchronized(this) { 709 mRequestRender = true; 710 notify(); 711 } 712 } 713 714 public void surfaceCreated() { 715 synchronized(this) { 716 mHasSurface = true; 717 notify(); 718 } 719 } 720 721 public void surfaceDestroyed() { 722 synchronized(this) { 723 mHasSurface = false; 724 notify(); 725 } 726 } 727 728 public void onPause() { 729 synchronized (this) { 730 mPaused = true; 731 } 732 } 733 734 public void onResume() { 735 synchronized (this) { 736 mPaused = false; 737 notify(); 738 } 739 } 740 741 public void onWindowResize(int w, int h) { 742 synchronized (this) { 743 mWidth = w; 744 mHeight = h; 745 mSizeChanged = true; 746 notify(); 747 } 748 } 749 750 public void requestExitAndWait() { 751 // don't call this from GLThread thread or it is a guaranteed 752 // deadlock! 753 synchronized(this) { 754 mDone = true; 755 notify(); 756 } 757 try { 758 join(); 759 } catch (InterruptedException ex) { 760 Thread.currentThread().interrupt(); 761 } 762 } 763 764 /** 765 * Queue an "event" to be run on the GL rendering thread. 766 * @param r the runnable to be run on the GL rendering thread. 767 */ 768 public void queueEvent(Runnable r) { 769 synchronized(this) { 770 mEventQueue.add(r); 771 } 772 } 773 774 private Runnable getEvent() { 775 synchronized(this) { 776 if (mEventQueue.size() > 0) { 777 return mEventQueue.remove(0); 778 } 779 780 } 781 return null; 782 } 783 784 private boolean mDone; 785 private boolean mPaused; 786 private boolean mHasSurface; 787 private int mWidth; 788 private int mHeight; 789 private int mRenderMode; 790 private boolean mRequestRender; 791 private Renderer mRenderer; 792 private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>(); 793 private EglHelper mEglHelper; 794 } 795 796 static class LogWriter extends Writer { 797 798 @Override public void close() { 799 flushBuilder(); 800 } 801 802 @Override public void flush() { 803 flushBuilder(); 804 } 805 806 @Override public void write(char[] buf, int offset, int count) { 807 for(int i = 0; i < count; i++) { 808 char c = buf[offset + i]; 809 if ( c == '\n') { 810 flushBuilder(); 811 } 812 else { 813 mBuilder.append(c); 814 } 815 } 816 } 817 818 private void flushBuilder() { 819 if (mBuilder.length() > 0) { 820 Log.v("GLSurfaceView", mBuilder.toString()); 821 mBuilder.delete(0, mBuilder.length()); 822 } 823 } 824 825 private StringBuilder mBuilder = new StringBuilder(); 826 } 827 828 private static final Semaphore sEglSemaphore = new Semaphore(1); 829 private boolean mSizeChanged = true; 830 831 private GLThread mGLThread; 832 private EGLConfigChooser mEGLConfigChooser; 833 private GLWrapper mGLWrapper; 834 private int mDebugFlags; 835} 836