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