HardwareRenderer.java revision 4e91a180be46c0c7c3bf398d4df4cbe2404216b5
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 18package android.view; 19 20import android.graphics.Canvas; 21import android.graphics.Paint; 22import android.graphics.Rect; 23import android.os.*; 24import android.util.EventLog; 25import android.util.Log; 26 27import javax.microedition.khronos.egl.EGL10; 28import javax.microedition.khronos.egl.EGL11; 29import javax.microedition.khronos.egl.EGLConfig; 30import javax.microedition.khronos.egl.EGLContext; 31import javax.microedition.khronos.egl.EGLDisplay; 32import javax.microedition.khronos.egl.EGLSurface; 33import javax.microedition.khronos.opengles.GL; 34 35/** 36 * Interface for rendering a ViewRoot using hardware acceleration. 37 * 38 * @hide 39 */ 40public abstract class HardwareRenderer { 41 static final String LOG_TAG = "HardwareRenderer"; 42 43 /** 44 * Turn on to only refresh the parts of the screen that need updating. 45 * When turned on the property defined by {@link #RENDER_DIRTY_REGIONS_PROPERTY} 46 * must also have the value "true". 47 */ 48 public static final boolean RENDER_DIRTY_REGIONS = true; 49 50 /** 51 * System property used to enable or disable dirty regions invalidation. 52 * This property is only queried if {@link #RENDER_DIRTY_REGIONS} is true. 53 * The default value of this property is assumed to be true. 54 * 55 * Possible values: 56 * "true", to enable partial invalidates 57 * "false", to disable partial invalidates 58 */ 59 static final String RENDER_DIRTY_REGIONS_PROPERTY = "hwui.render_dirty_regions"; 60 61 /** 62 * Turn on to draw dirty regions every other frame. 63 */ 64 private static final boolean DEBUG_DIRTY_REGION = false; 65 66 /** 67 * A process can set this flag to false to prevent the use of hardware 68 * rendering. 69 * 70 * @hide 71 */ 72 public static boolean sRendererDisabled = false; 73 74 private boolean mEnabled; 75 private boolean mRequested = true; 76 77 /** 78 * Invoke this method to disable hardware rendering in the current process. 79 * 80 * @hide 81 */ 82 public static void disable() { 83 sRendererDisabled = true; 84 } 85 86 /** 87 * Indicates whether hardware acceleration is available under any form for 88 * the view hierarchy. 89 * 90 * @return True if the view hierarchy can potentially be hardware accelerated, 91 * false otherwise 92 */ 93 public static boolean isAvailable() { 94 return GLES20Canvas.isAvailable(); 95 } 96 97 /** 98 * Destroys the hardware rendering context. 99 * 100 * @param full If true, destroys all associated resources. 101 */ 102 abstract void destroy(boolean full); 103 104 /** 105 * Initializes the hardware renderer for the specified surface. 106 * 107 * @param holder The holder for the surface to hardware accelerate. 108 * 109 * @return True if the initialization was successful, false otherwise. 110 */ 111 abstract boolean initialize(SurfaceHolder holder) throws Surface.OutOfResourcesException; 112 113 /** 114 * Updates the hardware renderer for the specified surface. 115 * 116 * @param holder The holder for the surface to hardware accelerate. 117 */ 118 abstract void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException; 119 120 /** 121 * Setup the hardware renderer for drawing. This is called for every 122 * frame to draw. 123 * 124 * @param width Width of the drawing surface. 125 * @param height Height of the drawing surface. 126 */ 127 abstract void setup(int width, int height); 128 129 /** 130 * Interface used to receive callbacks whenever a view is drawn by 131 * a hardware renderer instance. 132 */ 133 interface HardwareDrawCallbacks { 134 /** 135 * Invoked before a view is drawn by a hardware renderer. 136 * 137 * @param canvas The Canvas used to render the view. 138 */ 139 void onHardwarePreDraw(Canvas canvas); 140 141 /** 142 * Invoked after a view is drawn by a hardware renderer. 143 * 144 * @param canvas The Canvas used to render the view. 145 */ 146 void onHardwarePostDraw(Canvas canvas); 147 } 148 149 /** 150 * Draws the specified view. 151 * 152 * @param view The view to draw. 153 * @param attachInfo AttachInfo tied to the specified view. 154 * @param callbacks Callbacks invoked when drawing happens. 155 * @param dirty The dirty rectangle to update, can be null. 156 */ 157 abstract void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, 158 Rect dirty); 159 160 /** 161 * Creates a new display list that can be used to record batches of 162 * drawing operations. 163 * 164 * @return A new display list. 165 */ 166 abstract DisplayList createDisplayList(View v); 167 168 /** 169 * Creates a new hardware layer. 170 * 171 * @param width The minimum width of the layer 172 * @param height The minimum height of the layer 173 * @param isOpaque Whether the layer should be opaque or not 174 * 175 * @return A hardware layer 176 */ 177 abstract HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque); 178 179 /** 180 * Initializes the hardware renderer for the specified surface and setup the 181 * renderer for drawing, if needed. This is invoked when the ViewRoot has 182 * potentially lost the hardware renderer. The hardware renderer should be 183 * reinitialized and setup when the render {@link #isRequested()} and 184 * {@link #isEnabled()}. 185 * 186 * @param width The width of the drawing surface. 187 * @param height The height of the drawing surface. 188 * @param attachInfo The 189 * @param holder 190 */ 191 void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo, 192 SurfaceHolder holder) throws Surface.OutOfResourcesException { 193 if (isRequested()) { 194 // We lost the gl context, so recreate it. 195 if (!isEnabled()) { 196 if (initialize(holder)) { 197 setup(width, height); 198 } 199 } 200 } 201 } 202 203 /** 204 * Creates a hardware renderer using OpenGL. 205 * 206 * @param glVersion The version of OpenGL to use (1 for OpenGL 1, 11 for OpenGL 1.1, etc.) 207 * @param translucent True if the surface is translucent, false otherwise 208 * 209 * @return A hardware renderer backed by OpenGL. 210 */ 211 static HardwareRenderer createGlRenderer(int glVersion, boolean translucent) { 212 switch (glVersion) { 213 case 2: 214 return Gl20Renderer.create(translucent); 215 } 216 throw new IllegalArgumentException("Unknown GL version: " + glVersion); 217 } 218 219 /** 220 * Indicates whether hardware acceleration is currently enabled. 221 * 222 * @return True if hardware acceleration is in use, false otherwise. 223 */ 224 boolean isEnabled() { 225 return mEnabled; 226 } 227 228 /** 229 * Indicates whether hardware acceleration is currently enabled. 230 * 231 * @param enabled True if the hardware renderer is in use, false otherwise. 232 */ 233 void setEnabled(boolean enabled) { 234 mEnabled = enabled; 235 } 236 237 /** 238 * Indicates whether hardware acceleration is currently request but not 239 * necessarily enabled yet. 240 * 241 * @return True if requested, false otherwise. 242 */ 243 boolean isRequested() { 244 return mRequested; 245 } 246 247 /** 248 * Indicates whether hardware acceleration is currently requested but not 249 * necessarily enabled yet. 250 * 251 * @return True to request hardware acceleration, false otherwise. 252 */ 253 void setRequested(boolean requested) { 254 mRequested = requested; 255 } 256 257 @SuppressWarnings({"deprecation"}) 258 static abstract class GlRenderer extends HardwareRenderer { 259 // These values are not exposed in our EGL APIs 260 private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; 261 private static final int EGL_SURFACE_TYPE = 0x3033; 262 private static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400; 263 264 private static final int SURFACE_STATE_ERROR = 0; 265 private static final int SURFACE_STATE_SUCCESS = 1; 266 private static final int SURFACE_STATE_UPDATED = 2; 267 268 static EGLContext sEglContext; 269 static EGL10 sEgl; 270 static EGLDisplay sEglDisplay; 271 static EGLConfig sEglConfig; 272 273 private static Thread sEglThread; 274 275 EGLSurface mEglSurface; 276 277 GL mGl; 278 HardwareCanvas mCanvas; 279 int mFrameCount; 280 Paint mDebugPaint; 281 282 boolean mDirtyRegions; 283 284 final int mGlVersion; 285 final boolean mTranslucent; 286 287 private boolean mDestroyed; 288 289 private final Rect mRedrawClip = new Rect(); 290 291 GlRenderer(int glVersion, boolean translucent) { 292 mGlVersion = glVersion; 293 mTranslucent = translucent; 294 final String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true"); 295 //noinspection PointlessBooleanExpression,ConstantConditions 296 mDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty); 297 } 298 299 /** 300 * Return a string for the EGL error code, or the hex representation 301 * if the error is unknown. 302 * 303 * @param error The EGL error to convert into a String. 304 * 305 * @return An error string correponding to the EGL error code. 306 */ 307 static String getEGLErrorString(int error) { 308 switch (error) { 309 case EGL10.EGL_SUCCESS: 310 return "EGL_SUCCESS"; 311 case EGL10.EGL_NOT_INITIALIZED: 312 return "EGL_NOT_INITIALIZED"; 313 case EGL10.EGL_BAD_ACCESS: 314 return "EGL_BAD_ACCESS"; 315 case EGL10.EGL_BAD_ALLOC: 316 return "EGL_BAD_ALLOC"; 317 case EGL10.EGL_BAD_ATTRIBUTE: 318 return "EGL_BAD_ATTRIBUTE"; 319 case EGL10.EGL_BAD_CONFIG: 320 return "EGL_BAD_CONFIG"; 321 case EGL10.EGL_BAD_CONTEXT: 322 return "EGL_BAD_CONTEXT"; 323 case EGL10.EGL_BAD_CURRENT_SURFACE: 324 return "EGL_BAD_CURRENT_SURFACE"; 325 case EGL10.EGL_BAD_DISPLAY: 326 return "EGL_BAD_DISPLAY"; 327 case EGL10.EGL_BAD_MATCH: 328 return "EGL_BAD_MATCH"; 329 case EGL10.EGL_BAD_NATIVE_PIXMAP: 330 return "EGL_BAD_NATIVE_PIXMAP"; 331 case EGL10.EGL_BAD_NATIVE_WINDOW: 332 return "EGL_BAD_NATIVE_WINDOW"; 333 case EGL10.EGL_BAD_PARAMETER: 334 return "EGL_BAD_PARAMETER"; 335 case EGL10.EGL_BAD_SURFACE: 336 return "EGL_BAD_SURFACE"; 337 case EGL11.EGL_CONTEXT_LOST: 338 return "EGL_CONTEXT_LOST"; 339 default: 340 return "0x" + Integer.toHexString(error); 341 } 342 } 343 344 /** 345 * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)} 346 * is invoked and the requested flag is turned off. The error code is 347 * also logged as a warning. 348 */ 349 void checkEglErrors() { 350 if (isEnabled()) { 351 int error = sEgl.eglGetError(); 352 if (error != EGL10.EGL_SUCCESS) { 353 // something bad has happened revert to 354 // normal rendering. 355 fallback(error != EGL11.EGL_CONTEXT_LOST); 356 Log.w(LOG_TAG, "EGL error: " + getEGLErrorString(error)); 357 } 358 } 359 } 360 361 private void fallback(boolean fallback) { 362 destroy(true); 363 if (fallback) { 364 // we'll try again if it was context lost 365 setRequested(false); 366 Log.w(LOG_TAG, "Mountain View, we've had a problem here. " 367 + "Switching back to software rendering."); 368 } 369 } 370 371 @Override 372 boolean initialize(SurfaceHolder holder) throws Surface.OutOfResourcesException { 373 if (isRequested() && !isEnabled()) { 374 initializeEgl(); 375 mGl = createEglSurface(holder); 376 mDestroyed = false; 377 378 if (mGl != null) { 379 int err = sEgl.eglGetError(); 380 if (err != EGL10.EGL_SUCCESS) { 381 destroy(true); 382 setRequested(false); 383 } else { 384 if (mCanvas == null) { 385 mCanvas = createCanvas(); 386 } 387 if (mCanvas != null) { 388 setEnabled(true); 389 } else { 390 Log.w(LOG_TAG, "Hardware accelerated Canvas could not be created"); 391 } 392 } 393 394 return mCanvas != null; 395 } 396 } 397 return false; 398 } 399 400 @Override 401 void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException { 402 if (isRequested() && isEnabled()) { 403 createEglSurface(holder); 404 } 405 } 406 407 abstract GLES20Canvas createCanvas(); 408 409 void initializeEgl() { 410 if (sEglContext != null) return; 411 412 sEglThread = Thread.currentThread(); 413 sEgl = (EGL10) EGLContext.getEGL(); 414 415 // Get to the default display. 416 sEglDisplay = sEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); 417 418 if (sEglDisplay == EGL10.EGL_NO_DISPLAY) { 419 throw new RuntimeException("eglGetDisplay failed " 420 + getEGLErrorString(sEgl.eglGetError())); 421 } 422 423 // We can now initialize EGL for that display 424 int[] version = new int[2]; 425 if (!sEgl.eglInitialize(sEglDisplay, version)) { 426 throw new RuntimeException("eglInitialize failed " + 427 getEGLErrorString(sEgl.eglGetError())); 428 } 429 430 sEglConfig = getConfigChooser(mGlVersion).chooseConfig(sEgl, sEglDisplay); 431 if (sEglConfig == null) { 432 // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without 433 if (mDirtyRegions) { 434 mDirtyRegions = false; 435 436 sEglConfig = getConfigChooser(mGlVersion).chooseConfig(sEgl, sEglDisplay); 437 if (sEglConfig == null) { 438 throw new RuntimeException("eglConfig not initialized"); 439 } 440 } else { 441 throw new RuntimeException("eglConfig not initialized"); 442 } 443 } 444 445 /* 446 * Create an EGL context. We want to do this as rarely as we can, because an 447 * EGL context is a somewhat heavy object. 448 */ 449 sEglContext = createContext(sEgl, sEglDisplay, sEglConfig); 450 } 451 452 GL createEglSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException { 453 // Check preconditions. 454 if (sEgl == null) { 455 throw new RuntimeException("egl not initialized"); 456 } 457 if (sEglDisplay == null) { 458 throw new RuntimeException("eglDisplay not initialized"); 459 } 460 if (sEglConfig == null) { 461 throw new RuntimeException("eglConfig not initialized"); 462 } 463 if (Thread.currentThread() != sEglThread) { 464 throw new IllegalStateException("HardwareRenderer cannot be used " 465 + "from multiple threads"); 466 } 467 468 /* 469 * The window size has changed, so we need to create a new 470 * surface. 471 */ 472 if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) { 473 /* 474 * Unbind and destroy the old EGL surface, if 475 * there is one. 476 */ 477 sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE, 478 EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); 479 sEgl.eglDestroySurface(sEglDisplay, mEglSurface); 480 } 481 482 // Create an EGL surface we can render into. 483 mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, holder, null); 484 485 if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) { 486 int error = sEgl.eglGetError(); 487 if (error == EGL10.EGL_BAD_NATIVE_WINDOW) { 488 Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW."); 489 return null; 490 } 491 throw new RuntimeException("createWindowSurface failed " 492 + getEGLErrorString(error)); 493 } 494 495 /* 496 * Before we can issue GL commands, we need to make sure 497 * the context is current and bound to a surface. 498 */ 499 if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) { 500 throw new Surface.OutOfResourcesException("eglMakeCurrent failed " 501 + getEGLErrorString(sEgl.eglGetError())); 502 } 503 504 if (mDirtyRegions) { 505 if (!GLES20Canvas.preserveBackBuffer()) { 506 Log.w(LOG_TAG, "Backbuffer cannot be preserved"); 507 } 508 } 509 510 return sEglContext.getGL(); 511 } 512 513 EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) { 514 int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL10.EGL_NONE }; 515 516 return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, 517 mGlVersion != 0 ? attrib_list : null); 518 } 519 520 @Override 521 void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo, 522 SurfaceHolder holder) throws Surface.OutOfResourcesException { 523 if (isRequested()) { 524 checkEglErrors(); 525 super.initializeIfNeeded(width, height, attachInfo, holder); 526 } 527 } 528 529 @Override 530 void destroy(boolean full) { 531 if (full && mCanvas != null) { 532 mCanvas = null; 533 } 534 535 if (!isEnabled() || mDestroyed) return; 536 537 mDestroyed = true; 538 539 sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE, 540 EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); 541 sEgl.eglDestroySurface(sEglDisplay, mEglSurface); 542 543 mEglSurface = null; 544 mGl = null; 545 546 setEnabled(false); 547 } 548 549 @Override 550 void setup(int width, int height) { 551 mCanvas.setViewport(width, height); 552 } 553 554 boolean canDraw() { 555 return mGl != null && mCanvas != null; 556 } 557 558 void onPreDraw(Rect dirty) { 559 } 560 561 void onPostDraw() { 562 } 563 564 /** 565 * Defines the EGL configuration for this renderer. 566 * 567 * @return An {@link android.view.HardwareRenderer.GlRenderer.EglConfigChooser}. 568 */ 569 EglConfigChooser getConfigChooser(int glVersion) { 570 return new ComponentSizeChooser(glVersion, 8, 8, 8, 8, 0, 0, mDirtyRegions); 571 } 572 573 @Override 574 void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, 575 Rect dirty) { 576 if (canDraw()) { 577 if (!mDirtyRegions) { 578 dirty = null; 579 } 580 581 attachInfo.mDrawingTime = SystemClock.uptimeMillis(); 582 attachInfo.mIgnoreDirtyState = true; 583 view.mPrivateFlags |= View.DRAWN; 584 585 long startTime; 586 if (ViewDebug.DEBUG_PROFILE_DRAWING) { 587 startTime = SystemClock.elapsedRealtime(); 588 } 589 590 final int surfaceState = checkCurrent(); 591 if (surfaceState != SURFACE_STATE_ERROR) { 592 // We had to change the current surface and/or context, redraw everything 593 if (surfaceState == SURFACE_STATE_UPDATED) { 594 dirty = null; 595 } 596 597 onPreDraw(dirty); 598 599 HardwareCanvas canvas = mCanvas; 600 attachInfo.mHardwareCanvas = canvas; 601 602 int saveCount = canvas.save(); 603 callbacks.onHardwarePreDraw(canvas); 604 605 try { 606 view.mRecreateDisplayList = 607 (view.mPrivateFlags & View.INVALIDATED) == View.INVALIDATED; 608 view.mPrivateFlags &= ~View.INVALIDATED; 609 610 DisplayList displayList = view.getDisplayList(); 611 if (displayList != null) { 612 if (canvas.drawDisplayList(displayList, view.getWidth(), 613 view.getHeight(), mRedrawClip)) { 614 if (mRedrawClip.isEmpty() || view.getParent() == null) { 615 view.invalidate(); 616 } else { 617 view.getParent().invalidateChild(view, mRedrawClip); 618 } 619 mRedrawClip.setEmpty(); 620 } 621 } else { 622 // Shouldn't reach here 623 view.draw(canvas); 624 } 625 626 if (DEBUG_DIRTY_REGION) { 627 if (mDebugPaint == null) { 628 mDebugPaint = new Paint(); 629 mDebugPaint.setColor(0x7fff0000); 630 } 631 if (dirty != null && (mFrameCount++ & 1) == 0) { 632 canvas.drawRect(dirty, mDebugPaint); 633 } 634 } 635 } finally { 636 callbacks.onHardwarePostDraw(canvas); 637 canvas.restoreToCount(saveCount); 638 view.mRecreateDisplayList = false; 639 } 640 641 onPostDraw(); 642 643 if (ViewDebug.DEBUG_PROFILE_DRAWING) { 644 EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime); 645 } 646 647 attachInfo.mIgnoreDirtyState = false; 648 649 final long swapBuffersStartTime; 650 if (ViewDebug.DEBUG_LATENCY) { 651 swapBuffersStartTime = System.nanoTime(); 652 } 653 654 sEgl.eglSwapBuffers(sEglDisplay, mEglSurface); 655 656 if (ViewDebug.DEBUG_LATENCY) { 657 long now = System.nanoTime(); 658 Log.d(LOG_TAG, "Latency: Spent " 659 + ((now - swapBuffersStartTime) * 0.000001f) 660 + "ms waiting for eglSwapBuffers()"); 661 } 662 663 checkEglErrors(); 664 } 665 } 666 } 667 668 private int checkCurrent() { 669 // TODO: Don't check the current context when we have one per UI thread 670 // TODO: Use a threadlocal flag to know whether the surface has changed 671 if (!sEglContext.equals(sEgl.eglGetCurrentContext()) || 672 !mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) { 673 if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) { 674 fallback(true); 675 Log.e(LOG_TAG, "eglMakeCurrent failed " + 676 getEGLErrorString(sEgl.eglGetError())); 677 return SURFACE_STATE_ERROR; 678 } else { 679 return SURFACE_STATE_UPDATED; 680 } 681 } 682 return SURFACE_STATE_SUCCESS; 683 } 684 685 static abstract class EglConfigChooser { 686 final int[] mConfigSpec; 687 private final int mGlVersion; 688 689 EglConfigChooser(int glVersion, int[] configSpec) { 690 mGlVersion = glVersion; 691 mConfigSpec = filterConfigSpec(configSpec); 692 } 693 694 EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { 695 int[] index = new int[1]; 696 if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, index)) { 697 throw new IllegalArgumentException("eglChooseConfig failed " 698 + getEGLErrorString(egl.eglGetError())); 699 } 700 701 int numConfigs = index[0]; 702 if (numConfigs <= 0) { 703 throw new IllegalArgumentException("No configs match configSpec"); 704 } 705 706 EGLConfig[] configs = new EGLConfig[numConfigs]; 707 if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, index)) { 708 throw new IllegalArgumentException("eglChooseConfig failed " 709 + getEGLErrorString(egl.eglGetError())); 710 } 711 712 EGLConfig config = chooseConfig(egl, display, configs); 713 if (config == null) { 714 throw new IllegalArgumentException("No config chosen"); 715 } 716 717 return config; 718 } 719 720 abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs); 721 722 private int[] filterConfigSpec(int[] configSpec) { 723 if (mGlVersion != 2) { 724 return configSpec; 725 } 726 /* We know none of the subclasses define EGL_RENDERABLE_TYPE. 727 * And we know the configSpec is well formed. 728 */ 729 int len = configSpec.length; 730 int[] newConfigSpec = new int[len + 2]; 731 System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1); 732 newConfigSpec[len - 1] = EGL10.EGL_RENDERABLE_TYPE; 733 newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */ 734 newConfigSpec[len + 1] = EGL10.EGL_NONE; 735 return newConfigSpec; 736 } 737 } 738 739 /** 740 * Choose a configuration with exactly the specified r,g,b,a sizes, 741 * and at least the specified depth and stencil sizes. 742 */ 743 static class ComponentSizeChooser extends EglConfigChooser { 744 private int[] mValue; 745 746 private final int mRedSize; 747 private final int mGreenSize; 748 private final int mBlueSize; 749 private final int mAlphaSize; 750 private final int mDepthSize; 751 private final int mStencilSize; 752 private final boolean mDirtyRegions; 753 754 ComponentSizeChooser(int glVersion, int redSize, int greenSize, int blueSize, 755 int alphaSize, int depthSize, int stencilSize, boolean dirtyRegions) { 756 super(glVersion, new int[] { 757 EGL10.EGL_RED_SIZE, redSize, 758 EGL10.EGL_GREEN_SIZE, greenSize, 759 EGL10.EGL_BLUE_SIZE, blueSize, 760 EGL10.EGL_ALPHA_SIZE, alphaSize, 761 EGL10.EGL_DEPTH_SIZE, depthSize, 762 EGL10.EGL_STENCIL_SIZE, stencilSize, 763 EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT | 764 (dirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0), 765 EGL10.EGL_NONE }); 766 mValue = new int[1]; 767 mRedSize = redSize; 768 mGreenSize = greenSize; 769 mBlueSize = blueSize; 770 mAlphaSize = alphaSize; 771 mDepthSize = depthSize; 772 mStencilSize = stencilSize; 773 mDirtyRegions = dirtyRegions; 774 } 775 776 @Override 777 EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) { 778 for (EGLConfig config : configs) { 779 int d = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0); 780 int s = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0); 781 if (d >= mDepthSize && s >= mStencilSize) { 782 int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0); 783 int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0); 784 int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0); 785 int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0); 786 boolean backBuffer; 787 if (mDirtyRegions) { 788 int surfaceType = findConfigAttrib(egl, display, config, 789 EGL_SURFACE_TYPE, 0); 790 backBuffer = (surfaceType & EGL_SWAP_BEHAVIOR_PRESERVED_BIT) != 0; 791 } else { 792 backBuffer = true; 793 } 794 if (r >= mRedSize && g >= mGreenSize && b >= mBlueSize && a >= mAlphaSize 795 && backBuffer) { 796 return config; 797 } 798 } 799 } 800 return null; 801 } 802 803 private int findConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config, 804 int attribute, int defaultValue) { 805 if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) { 806 return mValue[0]; 807 } 808 809 return defaultValue; 810 } 811 } 812 } 813 814 /** 815 * Hardware renderer using OpenGL ES 2.0. 816 */ 817 static class Gl20Renderer extends GlRenderer { 818 private GLES20Canvas mGlCanvas; 819 820 Gl20Renderer(boolean translucent) { 821 super(2, translucent); 822 } 823 824 @Override 825 GLES20Canvas createCanvas() { 826 return mGlCanvas = new GLES20Canvas(mTranslucent); 827 } 828 829 @Override 830 boolean canDraw() { 831 return super.canDraw() && mGlCanvas != null; 832 } 833 834 @Override 835 void onPreDraw(Rect dirty) { 836 mGlCanvas.onPreDraw(dirty); 837 } 838 839 @Override 840 void onPostDraw() { 841 mGlCanvas.onPostDraw(); 842 } 843 844 @Override 845 void destroy(boolean full) { 846 try { 847 super.destroy(full); 848 } finally { 849 if (full && mGlCanvas != null) { 850 mGlCanvas = null; 851 } 852 } 853 } 854 855 @Override 856 DisplayList createDisplayList(View v) { 857 return new GLES20DisplayList(v); 858 } 859 860 @Override 861 HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) { 862 return new GLES20Layer(width, height, isOpaque); 863 } 864 865 static HardwareRenderer create(boolean translucent) { 866 if (GLES20Canvas.isAvailable()) { 867 return new Gl20Renderer(translucent); 868 } 869 return null; 870 } 871 } 872} 873