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