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