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