HardwareRenderer.java revision 5c13d89c1332fcc499379b9064b891187b75ca32
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 display list that can be used to record batches of 107 * drawing operations. 108 * 109 * @return A new display list. 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 the error is unknown. 220 * 221 * @param error The EGL error to convert into a String. 222 * 223 * @return An error string correponding to the EGL error code. 224 */ 225 static String getEGLErrorString(int error) { 226 switch (error) { 227 case EGL10.EGL_SUCCESS: 228 return "EGL_SUCCESS"; 229 case EGL10.EGL_NOT_INITIALIZED: 230 return "EGL_NOT_INITIALIZED"; 231 case EGL10.EGL_BAD_ACCESS: 232 return "EGL_BAD_ACCESS"; 233 case EGL10.EGL_BAD_ALLOC: 234 return "EGL_BAD_ALLOC"; 235 case EGL10.EGL_BAD_ATTRIBUTE: 236 return "EGL_BAD_ATTRIBUTE"; 237 case EGL10.EGL_BAD_CONFIG: 238 return "EGL_BAD_CONFIG"; 239 case EGL10.EGL_BAD_CONTEXT: 240 return "EGL_BAD_CONTEXT"; 241 case EGL10.EGL_BAD_CURRENT_SURFACE: 242 return "EGL_BAD_CURRENT_SURFACE"; 243 case EGL10.EGL_BAD_DISPLAY: 244 return "EGL_BAD_DISPLAY"; 245 case EGL10.EGL_BAD_MATCH: 246 return "EGL_BAD_MATCH"; 247 case EGL10.EGL_BAD_NATIVE_PIXMAP: 248 return "EGL_BAD_NATIVE_PIXMAP"; 249 case EGL10.EGL_BAD_NATIVE_WINDOW: 250 return "EGL_BAD_NATIVE_WINDOW"; 251 case EGL10.EGL_BAD_PARAMETER: 252 return "EGL_BAD_PARAMETER"; 253 case EGL10.EGL_BAD_SURFACE: 254 return "EGL_BAD_SURFACE"; 255 case EGL11.EGL_CONTEXT_LOST: 256 return "EGL_CONTEXT_LOST"; 257 default: 258 return "0x" + Integer.toHexString(error); 259 } 260 } 261 262 /** 263 * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)} 264 * is invoked and the requested flag is turned off. The error code is 265 * also logged as a warning. 266 */ 267 void checkEglErrors() { 268 if (isEnabled()) { 269 int error = sEgl.eglGetError(); 270 if (error != EGL10.EGL_SUCCESS) { 271 // something bad has happened revert to 272 // normal rendering. 273 destroy(true); 274 if (error != EGL11.EGL_CONTEXT_LOST) { 275 // we'll try again if it was context lost 276 setRequested(false); 277 } 278 Log.w(LOG_TAG, "EGL error: " + getEGLErrorString(error)); 279 } 280 } 281 } 282 283 @Override 284 boolean initialize(SurfaceHolder holder) { 285 if (isRequested() && !isEnabled()) { 286 initializeEgl(); 287 mGl = createEglSurface(holder); 288 mDestroyed = false; 289 290 if (mGl != null) { 291 int err = sEgl.eglGetError(); 292 if (err != EGL10.EGL_SUCCESS) { 293 destroy(true); 294 setRequested(false); 295 } else { 296 if (mCanvas == null) { 297 mCanvas = createCanvas(); 298 } 299 if (mCanvas != null) { 300 setEnabled(true); 301 } else { 302 Log.w(LOG_TAG, "Hardware accelerated Canvas could not be created"); 303 } 304 } 305 306 return mCanvas != null; 307 } 308 } 309 return false; 310 } 311 312 abstract GLES20Canvas createCanvas(); 313 314 void initializeEgl() { 315 if (sEglContext != null) return; 316 317 sEglThread = Thread.currentThread(); 318 sEgl = (EGL10) EGLContext.getEGL(); 319 320 // Get to the default display. 321 sEglDisplay = sEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); 322 323 if (sEglDisplay == EGL10.EGL_NO_DISPLAY) { 324 throw new RuntimeException("eglGetDisplay failed " 325 + getEGLErrorString(sEgl.eglGetError())); 326 } 327 328 // We can now initialize EGL for that display 329 int[] version = new int[2]; 330 if (!sEgl.eglInitialize(sEglDisplay, version)) { 331 throw new RuntimeException("eglInitialize failed " 332 + getEGLErrorString(sEgl.eglGetError())); 333 } 334 sEglConfig = getConfigChooser(mGlVersion).chooseConfig(sEgl, sEglDisplay); 335 336 /* 337 * Create an EGL context. We want to do this as rarely as we can, because an 338 * EGL context is a somewhat heavy object. 339 */ 340 sEglContext = createContext(sEgl, sEglDisplay, sEglConfig); 341 } 342 343 GL createEglSurface(SurfaceHolder holder) { 344 // Check preconditions. 345 if (sEgl == null) { 346 throw new RuntimeException("egl not initialized"); 347 } 348 if (sEglDisplay == null) { 349 throw new RuntimeException("eglDisplay not initialized"); 350 } 351 if (sEglConfig == null) { 352 throw new RuntimeException("mEglConfig not initialized"); 353 } 354 if (Thread.currentThread() != sEglThread) { 355 throw new IllegalStateException("HardwareRenderer cannot be used " 356 + "from multiple threads"); 357 } 358 359 /* 360 * The window size has changed, so we need to create a new 361 * surface. 362 */ 363 if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) { 364 /* 365 * Unbind and destroy the old EGL surface, if 366 * there is one. 367 */ 368 sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE, 369 EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); 370 sEgl.eglDestroySurface(sEglDisplay, mEglSurface); 371 } 372 373 // Create an EGL surface we can render into. 374 mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, holder, null); 375 376 if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) { 377 int error = sEgl.eglGetError(); 378 if (error == EGL10.EGL_BAD_NATIVE_WINDOW) { 379 Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW."); 380 return null; 381 } 382 throw new RuntimeException("createWindowSurface failed " 383 + getEGLErrorString(error)); 384 } 385 386 /* 387 * Before we can issue GL commands, we need to make sure 388 * the context is current and bound to a surface. 389 */ 390 if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) { 391 throw new RuntimeException("eglMakeCurrent failed " 392 + getEGLErrorString(sEgl.eglGetError())); 393 } 394 395 return sEglContext.getGL(); 396 } 397 398 EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) { 399 int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL10.EGL_NONE }; 400 401 return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, 402 mGlVersion != 0 ? attrib_list : null); 403 } 404 405 @Override 406 void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo, 407 SurfaceHolder holder) { 408 if (isRequested()) { 409 checkEglErrors(); 410 super.initializeIfNeeded(width, height, attachInfo, holder); 411 } 412 } 413 414 @Override 415 void destroy(boolean full) { 416 if (full && mCanvas != null) { 417 mCanvas = null; 418 } 419 420 if (!isEnabled() || mDestroyed) return; 421 422 mDestroyed = true; 423 424 sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE, 425 EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); 426 sEgl.eglDestroySurface(sEglDisplay, mEglSurface); 427 428 mEglSurface = null; 429 mGl = null; 430 431 setEnabled(false); 432 } 433 434 @Override 435 void setup(int width, int height) { 436 mCanvas.setViewport(width, height); 437 } 438 439 boolean canDraw() { 440 return mGl != null && mCanvas != null; 441 } 442 443 void onPreDraw() { 444 } 445 446 void onPostDraw() { 447 } 448 449 /** 450 * Defines the EGL configuration for this renderer. 451 * 452 * @return An {@link android.view.HardwareRenderer.GlRenderer.EglConfigChooser}. 453 */ 454 EglConfigChooser getConfigChooser(int glVersion) { 455 return new ComponentSizeChooser(glVersion, 8, 8, 8, 8, 0, 0); 456 } 457 458 @Override 459 void draw(View view, View.AttachInfo attachInfo, int yOffset) { 460 if (canDraw()) { 461 attachInfo.mDrawingTime = SystemClock.uptimeMillis(); 462 attachInfo.mIgnoreDirtyState = true; 463 view.mPrivateFlags |= View.DRAWN; 464 465 long startTime; 466 if (ViewDebug.DEBUG_PROFILE_DRAWING) { 467 startTime = SystemClock.elapsedRealtime(); 468 } 469 470 checkCurrent(); 471 472 onPreDraw(); 473 474 Canvas canvas = mCanvas; 475 int saveCount = canvas.save(); 476 canvas.translate(0, -yOffset); 477 478 try { 479 view.draw(canvas); 480 } finally { 481 canvas.restoreToCount(saveCount); 482 } 483 484 onPostDraw(); 485 486 if (ViewDebug.DEBUG_PROFILE_DRAWING) { 487 EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime); 488 } 489 490 attachInfo.mIgnoreDirtyState = false; 491 492 sEgl.eglSwapBuffers(sEglDisplay, mEglSurface); 493 checkEglErrors(); 494 } 495 } 496 497 private void checkCurrent() { 498 // TODO: Don't check the current context when we have one per UI thread 499 // TODO: Use a threadlocal flag to know whether the surface has changed 500 if (sEgl.eglGetCurrentContext() != sEglContext || 501 sEgl.eglGetCurrentSurface(EGL10.EGL_DRAW) != mEglSurface) { 502 if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) { 503 throw new RuntimeException("eglMakeCurrent failed " 504 + getEGLErrorString(sEgl.eglGetError())); 505 } 506 } 507 } 508 509 static abstract class EglConfigChooser { 510 final int[] mConfigSpec; 511 private final int mGlVersion; 512 513 EglConfigChooser(int glVersion, int[] configSpec) { 514 mGlVersion = glVersion; 515 mConfigSpec = filterConfigSpec(configSpec); 516 } 517 518 EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { 519 int[] index = new int[1]; 520 if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, index)) { 521 throw new IllegalArgumentException("eglChooseConfig failed " 522 + getEGLErrorString(egl.eglGetError())); 523 } 524 525 int numConfigs = index[0]; 526 if (numConfigs <= 0) { 527 throw new IllegalArgumentException("No configs match configSpec"); 528 } 529 530 EGLConfig[] configs = new EGLConfig[numConfigs]; 531 if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, index)) { 532 throw new IllegalArgumentException("eglChooseConfig failed " 533 + getEGLErrorString(egl.eglGetError())); 534 } 535 536 EGLConfig config = chooseConfig(egl, display, configs); 537 if (config == null) { 538 throw new IllegalArgumentException("No config chosen"); 539 } 540 541 return config; 542 } 543 544 abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs); 545 546 private int[] filterConfigSpec(int[] configSpec) { 547 if (mGlVersion != 2) { 548 return configSpec; 549 } 550 /* We know none of the subclasses define EGL_RENDERABLE_TYPE. 551 * And we know the configSpec is well formed. 552 */ 553 int len = configSpec.length; 554 int[] newConfigSpec = new int[len + 2]; 555 System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1); 556 newConfigSpec[len - 1] = EGL10.EGL_RENDERABLE_TYPE; 557 newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */ 558 newConfigSpec[len + 1] = EGL10.EGL_NONE; 559 return newConfigSpec; 560 } 561 } 562 563 /** 564 * Choose a configuration with exactly the specified r,g,b,a sizes, 565 * and at least the specified depth and stencil sizes. 566 */ 567 static class ComponentSizeChooser extends EglConfigChooser { 568 private int[] mValue; 569 570 private int mRedSize; 571 private int mGreenSize; 572 private int mBlueSize; 573 private int mAlphaSize; 574 private int mDepthSize; 575 private int mStencilSize; 576 577 ComponentSizeChooser(int glVersion, int redSize, int greenSize, int blueSize, 578 int alphaSize, int depthSize, int stencilSize) { 579 super(glVersion, new int[] { 580 EGL10.EGL_RED_SIZE, redSize, 581 EGL10.EGL_GREEN_SIZE, greenSize, 582 EGL10.EGL_BLUE_SIZE, blueSize, 583 EGL10.EGL_ALPHA_SIZE, alphaSize, 584 EGL10.EGL_DEPTH_SIZE, depthSize, 585 EGL10.EGL_STENCIL_SIZE, stencilSize, 586 EGL10.EGL_NONE }); 587 mValue = new int[1]; 588 mRedSize = redSize; 589 mGreenSize = greenSize; 590 mBlueSize = blueSize; 591 mAlphaSize = alphaSize; 592 mDepthSize = depthSize; 593 mStencilSize = stencilSize; 594 } 595 596 @Override 597 EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) { 598 for (EGLConfig config : configs) { 599 int d = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0); 600 int s = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0); 601 if (d >= mDepthSize && s >= mStencilSize) { 602 int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0); 603 int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0); 604 int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0); 605 int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0); 606 if (r >= mRedSize && g >= mGreenSize && b >= mBlueSize && a >= mAlphaSize) { 607 return config; 608 } 609 } 610 } 611 return null; 612 } 613 614 private int findConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config, 615 int attribute, int defaultValue) { 616 if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) { 617 return mValue[0]; 618 } 619 620 return defaultValue; 621 } 622 } 623 } 624 625 /** 626 * Hardware renderer using OpenGL ES 2.0. 627 */ 628 static class Gl20Renderer extends GlRenderer { 629 private GLES20Canvas mGlCanvas; 630 631 Gl20Renderer(boolean translucent) { 632 super(2, translucent); 633 } 634 635 @Override 636 GLES20Canvas createCanvas() { 637 return mGlCanvas = new GLES20Canvas(mTranslucent); 638 } 639 640 @Override 641 void onPreDraw() { 642 mGlCanvas.onPreDraw(); 643 } 644 645 @Override 646 void onPostDraw() { 647 mGlCanvas.onPostDraw(); 648 } 649 650 @Override 651 DisplayList createDisplayList() { 652 return new GLES20DisplayList(); 653 } 654 655 static HardwareRenderer create(boolean translucent) { 656 if (GLES20Canvas.isAvailable()) { 657 return new Gl20Renderer(translucent); 658 } 659 return null; 660 } 661 } 662} 663