1 /* 2 * Copyright (C) 2012 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 package com.android.server.power; 18 19 import com.android.server.display.DisplayManagerService; 20 import com.android.server.display.DisplayTransactionListener; 21 22 import android.graphics.Bitmap; 23 import android.graphics.PixelFormat; 24 import android.opengl.EGL14; 25 import android.opengl.EGLConfig; 26 import android.opengl.EGLContext; 27 import android.opengl.EGLDisplay; 28 import android.opengl.EGLSurface; 29 import android.opengl.GLES10; 30 import android.opengl.GLUtils; 31 import android.os.Looper; 32 import android.util.FloatMath; 33 import android.util.Slog; 34 import android.view.Display; 35 import android.view.DisplayInfo; 36 import android.view.Surface; 37 import android.view.SurfaceSession; 38 39 import java.io.PrintWriter; 40 import java.nio.ByteBuffer; 41 import java.nio.ByteOrder; 42 import java.nio.FloatBuffer; 43 44 /** 45 * Bzzzoooop! *crackle* 46 * <p> 47 * Animates a screen transition from on to off or off to on by applying 48 * some GL transformations to a screenshot. 49 * </p><p> 50 * This component must only be created or accessed by the {@link Looper} thread 51 * that belongs to the {@link DisplayPowerController}. 52 * </p> 53 */ 54 final class ElectronBeam { 55 private static final String TAG = "ElectronBeam"; 56 57 private static final boolean DEBUG = false; 58 59 // The layer for the electron beam surface. 60 // This is currently hardcoded to be one layer above the boot animation. 61 private static final int ELECTRON_BEAM_LAYER = 0x40000001; 62 63 // The relative proportion of the animation to spend performing 64 // the horizontal stretch effect. The remainder is spent performing 65 // the vertical stretch effect. 66 private static final float HSTRETCH_DURATION = 0.5f; 67 private static final float VSTRETCH_DURATION = 1.0f - HSTRETCH_DURATION; 68 69 // The number of frames to draw when preparing the animation so that it will 70 // be ready to run smoothly. We use 3 frames because we are triple-buffered. 71 // See code for details. 72 private static final int DEJANK_FRAMES = 3; 73 74 // Set to true when the animation context has been fully prepared. 75 private boolean mPrepared; 76 private int mMode; 77 78 private final DisplayManagerService mDisplayManager; 79 private int mDisplayLayerStack; // layer stack associated with primary display 80 private int mDisplayWidth; // real width, not rotated 81 private int mDisplayHeight; // real height, not rotated 82 private SurfaceSession mSurfaceSession; 83 private Surface mSurface; 84 private NaturalSurfaceLayout mSurfaceLayout; 85 private EGLDisplay mEglDisplay; 86 private EGLConfig mEglConfig; 87 private EGLContext mEglContext; 88 private EGLSurface mEglSurface; 89 private boolean mSurfaceVisible; 90 private float mSurfaceAlpha; 91 92 // Texture names. We only use one texture, which contains the screenshot. 93 private final int[] mTexNames = new int[1]; 94 private boolean mTexNamesGenerated; 95 96 // Vertex and corresponding texture coordinates. 97 // We have 4 2D vertices, so 8 elements. The vertices form a quad. 98 private final FloatBuffer mVertexBuffer = createNativeFloatBuffer(8); 99 private final FloatBuffer mTexCoordBuffer = createNativeFloatBuffer(8); 100 101 /** 102 * Animates an electron beam warming up. 103 */ 104 public static final int MODE_WARM_UP = 0; 105 106 /** 107 * Animates an electron beam shutting off. 108 */ 109 public static final int MODE_COOL_DOWN = 1; 110 111 /** 112 * Animates a simple dim layer to fade the contents of the screen in or out progressively. 113 */ 114 public static final int MODE_FADE = 2; 115 ElectronBeam(DisplayManagerService displayManager)116 public ElectronBeam(DisplayManagerService displayManager) { 117 mDisplayManager = displayManager; 118 } 119 120 /** 121 * Warms up the electron beam in preparation for turning on or off. 122 * This method prepares a GL context, and captures a screen shot. 123 * 124 * @param mode The desired mode for the upcoming animation. 125 * @return True if the electron beam is ready, false if it is uncontrollable. 126 */ prepare(int mode)127 public boolean prepare(int mode) { 128 if (DEBUG) { 129 Slog.d(TAG, "prepare: mode=" + mode); 130 } 131 132 mMode = mode; 133 134 // Get the display size and layer stack. 135 // This is not expected to change while the electron beam surface is showing. 136 DisplayInfo displayInfo = mDisplayManager.getDisplayInfo(Display.DEFAULT_DISPLAY); 137 mDisplayLayerStack = displayInfo.layerStack; 138 mDisplayWidth = displayInfo.getNaturalWidth(); 139 mDisplayHeight = displayInfo.getNaturalHeight(); 140 141 // Prepare the surface for drawing. 142 if (!tryPrepare()) { 143 dismiss(); 144 return false; 145 } 146 147 // Done. 148 mPrepared = true; 149 150 // Dejanking optimization. 151 // Some GL drivers can introduce a lot of lag in the first few frames as they 152 // initialize their state and allocate graphics buffers for rendering. 153 // Work around this problem by rendering the first frame of the animation a few 154 // times. The rest of the animation should run smoothly thereafter. 155 // The frames we draw here aren't visible because we are essentially just 156 // painting the screenshot as-is. 157 if (mode == MODE_COOL_DOWN) { 158 for (int i = 0; i < DEJANK_FRAMES; i++) { 159 draw(1.0f); 160 } 161 } 162 return true; 163 } 164 tryPrepare()165 private boolean tryPrepare() { 166 if (createSurface()) { 167 if (mMode == MODE_FADE) { 168 return true; 169 } 170 return createEglContext() 171 && createEglSurface() 172 && captureScreenshotTextureAndSetViewport(); 173 } 174 return false; 175 } 176 177 /** 178 * Dismisses the electron beam animation surface and cleans up. 179 * 180 * To prevent stray photons from leaking out after the electron beam has been 181 * turned off, it is a good idea to defer dismissing the animation until the 182 * electron beam has been turned back on fully. 183 */ dismiss()184 public void dismiss() { 185 if (DEBUG) { 186 Slog.d(TAG, "dismiss"); 187 } 188 189 destroyScreenshotTexture(); 190 destroyEglSurface(); 191 destroySurface(); 192 mPrepared = false; 193 } 194 195 /** 196 * Draws an animation frame showing the electron beam activated at the 197 * specified level. 198 * 199 * @param level The electron beam level. 200 * @return True if successful. 201 */ draw(float level)202 public boolean draw(float level) { 203 if (DEBUG) { 204 Slog.d(TAG, "drawFrame: level=" + level); 205 } 206 207 if (!mPrepared) { 208 return false; 209 } 210 211 if (mMode == MODE_FADE) { 212 return showSurface(1.0f - level); 213 } 214 215 if (!attachEglContext()) { 216 return false; 217 } 218 try { 219 // Clear frame to solid black. 220 GLES10.glClearColor(0f, 0f, 0f, 1f); 221 GLES10.glClear(GLES10.GL_COLOR_BUFFER_BIT); 222 223 // Draw the frame. 224 if (level < HSTRETCH_DURATION) { 225 drawHStretch(1.0f - (level / HSTRETCH_DURATION)); 226 } else { 227 drawVStretch(1.0f - ((level - HSTRETCH_DURATION) / VSTRETCH_DURATION)); 228 } 229 if (checkGlErrors("drawFrame")) { 230 return false; 231 } 232 233 EGL14.eglSwapBuffers(mEglDisplay, mEglSurface); 234 } finally { 235 detachEglContext(); 236 } 237 return showSurface(1.0f); 238 } 239 240 /** 241 * Draws a frame where the content of the electron beam is collapsing inwards upon 242 * itself vertically with red / green / blue channels dispersing and eventually 243 * merging down to a single horizontal line. 244 * 245 * @param stretch The stretch factor. 0.0 is no collapse, 1.0 is full collapse. 246 */ drawVStretch(float stretch)247 private void drawVStretch(float stretch) { 248 // compute interpolation scale factors for each color channel 249 final float ar = scurve(stretch, 7.5f); 250 final float ag = scurve(stretch, 8.0f); 251 final float ab = scurve(stretch, 8.5f); 252 if (DEBUG) { 253 Slog.d(TAG, "drawVStretch: stretch=" + stretch 254 + ", ar=" + ar + ", ag=" + ag + ", ab=" + ab); 255 } 256 257 // set blending 258 GLES10.glBlendFunc(GLES10.GL_ONE, GLES10.GL_ONE); 259 GLES10.glEnable(GLES10.GL_BLEND); 260 261 // bind vertex buffer 262 GLES10.glVertexPointer(2, GLES10.GL_FLOAT, 0, mVertexBuffer); 263 GLES10.glEnableClientState(GLES10.GL_VERTEX_ARRAY); 264 265 // bind texture and set blending for drawing planes 266 GLES10.glBindTexture(GLES10.GL_TEXTURE_2D, mTexNames[0]); 267 GLES10.glTexEnvx(GLES10.GL_TEXTURE_ENV, GLES10.GL_TEXTURE_ENV_MODE, 268 mMode == MODE_WARM_UP ? GLES10.GL_MODULATE : GLES10.GL_REPLACE); 269 GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D, 270 GLES10.GL_TEXTURE_MAG_FILTER, GLES10.GL_LINEAR); 271 GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D, 272 GLES10.GL_TEXTURE_MIN_FILTER, GLES10.GL_LINEAR); 273 GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D, 274 GLES10.GL_TEXTURE_WRAP_S, GLES10.GL_CLAMP_TO_EDGE); 275 GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D, 276 GLES10.GL_TEXTURE_WRAP_T, GLES10.GL_CLAMP_TO_EDGE); 277 GLES10.glEnable(GLES10.GL_TEXTURE_2D); 278 GLES10.glTexCoordPointer(2, GLES10.GL_FLOAT, 0, mTexCoordBuffer); 279 GLES10.glEnableClientState(GLES10.GL_TEXTURE_COORD_ARRAY); 280 281 // draw the red plane 282 setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ar); 283 GLES10.glColorMask(true, false, false, true); 284 GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); 285 286 // draw the green plane 287 setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ag); 288 GLES10.glColorMask(false, true, false, true); 289 GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); 290 291 // draw the blue plane 292 setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ab); 293 GLES10.glColorMask(false, false, true, true); 294 GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); 295 296 // clean up after drawing planes 297 GLES10.glDisable(GLES10.GL_TEXTURE_2D); 298 GLES10.glDisableClientState(GLES10.GL_TEXTURE_COORD_ARRAY); 299 GLES10.glColorMask(true, true, true, true); 300 301 // draw the white highlight (we use the last vertices) 302 if (mMode == MODE_COOL_DOWN) { 303 GLES10.glColor4f(ag, ag, ag, 1.0f); 304 GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); 305 } 306 307 // clean up 308 GLES10.glDisableClientState(GLES10.GL_VERTEX_ARRAY); 309 GLES10.glDisable(GLES10.GL_BLEND); 310 } 311 312 /** 313 * Draws a frame where the electron beam has been stretched out into 314 * a thin white horizontal line that fades as it expands outwards. 315 * 316 * @param stretch The stretch factor. 0.0 is no stretch / no fade, 317 * 1.0 is maximum stretch / maximum fade. 318 */ drawHStretch(float stretch)319 private void drawHStretch(float stretch) { 320 // compute interpolation scale factor 321 final float ag = scurve(stretch, 8.0f); 322 if (DEBUG) { 323 Slog.d(TAG, "drawHStretch: stretch=" + stretch + ", ag=" + ag); 324 } 325 326 if (stretch < 1.0f) { 327 // bind vertex buffer 328 GLES10.glVertexPointer(2, GLES10.GL_FLOAT, 0, mVertexBuffer); 329 GLES10.glEnableClientState(GLES10.GL_VERTEX_ARRAY); 330 331 // draw narrow fading white line 332 setHStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ag); 333 GLES10.glColor4f(1.0f - ag, 1.0f - ag, 1.0f - ag, 1.0f); 334 GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); 335 336 // clean up 337 GLES10.glDisableClientState(GLES10.GL_VERTEX_ARRAY); 338 } 339 } 340 setVStretchQuad(FloatBuffer vtx, float dw, float dh, float a)341 private static void setVStretchQuad(FloatBuffer vtx, float dw, float dh, float a) { 342 final float w = dw + (dw * a); 343 final float h = dh - (dh * a); 344 final float x = (dw - w) * 0.5f; 345 final float y = (dh - h) * 0.5f; 346 setQuad(vtx, x, y, w, h); 347 } 348 setHStretchQuad(FloatBuffer vtx, float dw, float dh, float a)349 private static void setHStretchQuad(FloatBuffer vtx, float dw, float dh, float a) { 350 final float w = dw + (dw * a); 351 final float h = 1.0f; 352 final float x = (dw - w) * 0.5f; 353 final float y = (dh - h) * 0.5f; 354 setQuad(vtx, x, y, w, h); 355 } 356 setQuad(FloatBuffer vtx, float x, float y, float w, float h)357 private static void setQuad(FloatBuffer vtx, float x, float y, float w, float h) { 358 if (DEBUG) { 359 Slog.d(TAG, "setQuad: x=" + x + ", y=" + y + ", w=" + w + ", h=" + h); 360 } 361 vtx.put(0, x); 362 vtx.put(1, y); 363 vtx.put(2, x); 364 vtx.put(3, y + h); 365 vtx.put(4, x + w); 366 vtx.put(5, y + h); 367 vtx.put(6, x + w); 368 vtx.put(7, y); 369 } 370 captureScreenshotTextureAndSetViewport()371 private boolean captureScreenshotTextureAndSetViewport() { 372 // TODO: Use a SurfaceTexture to avoid the extra texture upload. 373 Bitmap bitmap = Surface.screenshot(mDisplayWidth, mDisplayHeight, 374 0, ELECTRON_BEAM_LAYER - 1); 375 if (bitmap == null) { 376 Slog.e(TAG, "Could not take a screenshot!"); 377 return false; 378 } 379 try { 380 if (!attachEglContext()) { 381 return false; 382 } 383 try { 384 if (!mTexNamesGenerated) { 385 GLES10.glGenTextures(1, mTexNames, 0); 386 if (checkGlErrors("glGenTextures")) { 387 return false; 388 } 389 mTexNamesGenerated = true; 390 } 391 392 GLES10.glBindTexture(GLES10.GL_TEXTURE_2D, mTexNames[0]); 393 if (checkGlErrors("glBindTexture")) { 394 return false; 395 } 396 397 float u = 1.0f; 398 float v = 1.0f; 399 GLUtils.texImage2D(GLES10.GL_TEXTURE_2D, 0, bitmap, 0); 400 if (checkGlErrors("glTexImage2D, first try", false)) { 401 // Try a power of two size texture instead. 402 int tw = nextPowerOfTwo(mDisplayWidth); 403 int th = nextPowerOfTwo(mDisplayHeight); 404 int format = GLUtils.getInternalFormat(bitmap); 405 GLES10.glTexImage2D(GLES10.GL_TEXTURE_2D, 0, 406 format, tw, th, 0, 407 format, GLES10.GL_UNSIGNED_BYTE, null); 408 if (checkGlErrors("glTexImage2D, second try")) { 409 return false; 410 } 411 412 GLUtils.texSubImage2D(GLES10.GL_TEXTURE_2D, 0, 0, 0, bitmap); 413 if (checkGlErrors("glTexSubImage2D")) { 414 return false; 415 } 416 417 u = (float)mDisplayWidth / tw; 418 v = (float)mDisplayHeight / th; 419 } 420 421 // Set up texture coordinates for a quad. 422 // We might need to change this if the texture ends up being 423 // a different size from the display for some reason. 424 mTexCoordBuffer.put(0, 0f); 425 mTexCoordBuffer.put(1, v); 426 mTexCoordBuffer.put(2, 0f); 427 mTexCoordBuffer.put(3, 0f); 428 mTexCoordBuffer.put(4, u); 429 mTexCoordBuffer.put(5, 0f); 430 mTexCoordBuffer.put(6, u); 431 mTexCoordBuffer.put(7, v); 432 433 // Set up our viewport. 434 GLES10.glViewport(0, 0, mDisplayWidth, mDisplayHeight); 435 GLES10.glMatrixMode(GLES10.GL_PROJECTION); 436 GLES10.glLoadIdentity(); 437 GLES10.glOrthof(0, mDisplayWidth, 0, mDisplayHeight, 0, 1); 438 GLES10.glMatrixMode(GLES10.GL_MODELVIEW); 439 GLES10.glLoadIdentity(); 440 GLES10.glMatrixMode(GLES10.GL_TEXTURE); 441 GLES10.glLoadIdentity(); 442 } finally { 443 detachEglContext(); 444 } 445 } finally { 446 bitmap.recycle(); 447 } 448 return true; 449 } 450 destroyScreenshotTexture()451 private void destroyScreenshotTexture() { 452 if (mTexNamesGenerated) { 453 mTexNamesGenerated = false; 454 if (attachEglContext()) { 455 try { 456 GLES10.glDeleteTextures(1, mTexNames, 0); 457 checkGlErrors("glDeleteTextures"); 458 } finally { 459 detachEglContext(); 460 } 461 } 462 } 463 } 464 createEglContext()465 private boolean createEglContext() { 466 if (mEglDisplay == null) { 467 mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); 468 if (mEglDisplay == EGL14.EGL_NO_DISPLAY) { 469 logEglError("eglGetDisplay"); 470 return false; 471 } 472 473 int[] version = new int[2]; 474 if (!EGL14.eglInitialize(mEglDisplay, version, 0, version, 1)) { 475 mEglDisplay = null; 476 logEglError("eglInitialize"); 477 return false; 478 } 479 } 480 481 if (mEglConfig == null) { 482 int[] eglConfigAttribList = new int[] { 483 EGL14.EGL_RED_SIZE, 8, 484 EGL14.EGL_GREEN_SIZE, 8, 485 EGL14.EGL_BLUE_SIZE, 8, 486 EGL14.EGL_ALPHA_SIZE, 8, 487 EGL14.EGL_NONE 488 }; 489 int[] numEglConfigs = new int[1]; 490 EGLConfig[] eglConfigs = new EGLConfig[1]; 491 if (!EGL14.eglChooseConfig(mEglDisplay, eglConfigAttribList, 0, 492 eglConfigs, 0, eglConfigs.length, numEglConfigs, 0)) { 493 logEglError("eglChooseConfig"); 494 return false; 495 } 496 mEglConfig = eglConfigs[0]; 497 } 498 499 if (mEglContext == null) { 500 int[] eglContextAttribList = new int[] { 501 EGL14.EGL_NONE 502 }; 503 mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig, 504 EGL14.EGL_NO_CONTEXT, eglContextAttribList, 0); 505 if (mEglContext == null) { 506 logEglError("eglCreateContext"); 507 return false; 508 } 509 } 510 return true; 511 } 512 513 /* not used because it is too expensive to create / destroy contexts all of the time 514 private void destroyEglContext() { 515 if (mEglContext != null) { 516 if (!EGL14.eglDestroyContext(mEglDisplay, mEglContext)) { 517 logEglError("eglDestroyContext"); 518 } 519 mEglContext = null; 520 } 521 }*/ 522 createSurface()523 private boolean createSurface() { 524 if (mSurfaceSession == null) { 525 mSurfaceSession = new SurfaceSession(); 526 } 527 528 Surface.openTransaction(); 529 try { 530 if (mSurface == null) { 531 try { 532 int flags; 533 if (mMode == MODE_FADE) { 534 flags = Surface.FX_SURFACE_DIM | Surface.HIDDEN; 535 } else { 536 flags = Surface.OPAQUE | Surface.HIDDEN; 537 } 538 mSurface = new Surface(mSurfaceSession, 539 "ElectronBeam", mDisplayWidth, mDisplayHeight, 540 PixelFormat.OPAQUE, flags); 541 } catch (Surface.OutOfResourcesException ex) { 542 Slog.e(TAG, "Unable to create surface.", ex); 543 return false; 544 } 545 } 546 547 mSurface.setLayerStack(mDisplayLayerStack); 548 mSurface.setSize(mDisplayWidth, mDisplayHeight); 549 550 mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManager, mSurface); 551 mSurfaceLayout.onDisplayTransaction(); 552 } finally { 553 Surface.closeTransaction(); 554 } 555 return true; 556 } 557 createEglSurface()558 private boolean createEglSurface() { 559 if (mEglSurface == null) { 560 int[] eglSurfaceAttribList = new int[] { 561 EGL14.EGL_NONE 562 }; 563 mEglSurface = EGL14.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface, 564 eglSurfaceAttribList, 0); 565 if (mEglSurface == null) { 566 logEglError("eglCreateWindowSurface"); 567 return false; 568 } 569 } 570 return true; 571 } 572 destroyEglSurface()573 private void destroyEglSurface() { 574 if (mEglSurface != null) { 575 if (!EGL14.eglDestroySurface(mEglDisplay, mEglSurface)) { 576 logEglError("eglDestroySurface"); 577 } 578 mEglSurface = null; 579 } 580 } 581 destroySurface()582 private void destroySurface() { 583 if (mSurface != null) { 584 mSurfaceLayout.dispose(); 585 mSurfaceLayout = null; 586 Surface.openTransaction(); 587 try { 588 mSurface.destroy(); 589 } finally { 590 Surface.closeTransaction(); 591 } 592 mSurface = null; 593 mSurfaceVisible = false; 594 mSurfaceAlpha = 0f; 595 } 596 } 597 showSurface(float alpha)598 private boolean showSurface(float alpha) { 599 if (!mSurfaceVisible || mSurfaceAlpha != alpha) { 600 Surface.openTransaction(); 601 try { 602 mSurface.setLayer(ELECTRON_BEAM_LAYER); 603 mSurface.setAlpha(alpha); 604 mSurface.show(); 605 } finally { 606 Surface.closeTransaction(); 607 } 608 mSurfaceVisible = true; 609 mSurfaceAlpha = alpha; 610 } 611 return true; 612 } 613 attachEglContext()614 private boolean attachEglContext() { 615 if (mEglSurface == null) { 616 return false; 617 } 618 if (!EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 619 logEglError("eglMakeCurrent"); 620 return false; 621 } 622 return true; 623 } 624 detachEglContext()625 private void detachEglContext() { 626 if (mEglDisplay != null) { 627 EGL14.eglMakeCurrent(mEglDisplay, 628 EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT); 629 } 630 } 631 632 /** 633 * Interpolates a value in the range 0 .. 1 along a sigmoid curve 634 * yielding a result in the range 0 .. 1 scaled such that: 635 * scurve(0) == 0, scurve(0.5) == 0.5, scurve(1) == 1. 636 */ scurve(float value, float s)637 private static float scurve(float value, float s) { 638 // A basic sigmoid has the form y = 1.0f / FloatMap.exp(-x * s). 639 // Here we take the input datum and shift it by 0.5 so that the 640 // domain spans the range -0.5 .. 0.5 instead of 0 .. 1. 641 final float x = value - 0.5f; 642 643 // Next apply the sigmoid function to the scaled value 644 // which produces a value in the range 0 .. 1 so we subtract 645 // 0.5 to get a value in the range -0.5 .. 0.5 instead. 646 final float y = sigmoid(x, s) - 0.5f; 647 648 // To obtain the desired boundary conditions we need to scale 649 // the result so that it fills a range of -1 .. 1. 650 final float v = sigmoid(0.5f, s) - 0.5f; 651 652 // And finally remap the value back to a range of 0 .. 1. 653 return y / v * 0.5f + 0.5f; 654 } 655 sigmoid(float x, float s)656 private static float sigmoid(float x, float s) { 657 return 1.0f / (1.0f + FloatMath.exp(-x * s)); 658 } 659 nextPowerOfTwo(int value)660 private static int nextPowerOfTwo(int value) { 661 return 1 << (32 - Integer.numberOfLeadingZeros(value)); 662 } 663 createNativeFloatBuffer(int size)664 private static FloatBuffer createNativeFloatBuffer(int size) { 665 ByteBuffer bb = ByteBuffer.allocateDirect(size * 4); 666 bb.order(ByteOrder.nativeOrder()); 667 return bb.asFloatBuffer(); 668 } 669 logEglError(String func)670 private static void logEglError(String func) { 671 Slog.e(TAG, func + " failed: error " + EGL14.eglGetError(), new Throwable()); 672 } 673 checkGlErrors(String func)674 private static boolean checkGlErrors(String func) { 675 return checkGlErrors(func, true); 676 } 677 checkGlErrors(String func, boolean log)678 private static boolean checkGlErrors(String func, boolean log) { 679 boolean hadError = false; 680 int error; 681 while ((error = GLES10.glGetError()) != GLES10.GL_NO_ERROR) { 682 if (log) { 683 Slog.e(TAG, func + " failed: error " + error, new Throwable()); 684 } 685 hadError = true; 686 } 687 return hadError; 688 } 689 dump(PrintWriter pw)690 public void dump(PrintWriter pw) { 691 pw.println(); 692 pw.println("Electron Beam State:"); 693 pw.println(" mPrepared=" + mPrepared); 694 pw.println(" mMode=" + mMode); 695 pw.println(" mDisplayLayerStack=" + mDisplayLayerStack); 696 pw.println(" mDisplayWidth=" + mDisplayWidth); 697 pw.println(" mDisplayHeight=" + mDisplayHeight); 698 pw.println(" mSurfaceVisible=" + mSurfaceVisible); 699 pw.println(" mSurfaceAlpha=" + mSurfaceAlpha); 700 } 701 702 /** 703 * Keeps a surface aligned with the natural orientation of the device. 704 * Updates the position and transformation of the matrix whenever the display 705 * is rotated. This is a little tricky because the display transaction 706 * callback can be invoked on any thread, not necessarily the thread that 707 * owns the electron beam. 708 */ 709 private static final class NaturalSurfaceLayout implements DisplayTransactionListener { 710 private final DisplayManagerService mDisplayManager; 711 private Surface mSurface; 712 NaturalSurfaceLayout(DisplayManagerService displayManager, Surface surface)713 public NaturalSurfaceLayout(DisplayManagerService displayManager, Surface surface) { 714 mDisplayManager = displayManager; 715 mSurface = surface; 716 mDisplayManager.registerDisplayTransactionListener(this); 717 } 718 dispose()719 public void dispose() { 720 synchronized (this) { 721 mSurface = null; 722 } 723 mDisplayManager.unregisterDisplayTransactionListener(this); 724 } 725 726 @Override onDisplayTransaction()727 public void onDisplayTransaction() { 728 synchronized (this) { 729 if (mSurface == null) { 730 return; 731 } 732 733 DisplayInfo displayInfo = mDisplayManager.getDisplayInfo(Display.DEFAULT_DISPLAY); 734 switch (displayInfo.rotation) { 735 case Surface.ROTATION_0: 736 mSurface.setPosition(0, 0); 737 mSurface.setMatrix(1, 0, 0, 1); 738 break; 739 case Surface.ROTATION_90: 740 mSurface.setPosition(0, displayInfo.logicalHeight); 741 mSurface.setMatrix(0, -1, 1, 0); 742 break; 743 case Surface.ROTATION_180: 744 mSurface.setPosition(displayInfo.logicalWidth, displayInfo.logicalHeight); 745 mSurface.setMatrix(-1, 0, 0, -1); 746 break; 747 case Surface.ROTATION_270: 748 mSurface.setPosition(displayInfo.logicalWidth, 0); 749 mSurface.setMatrix(0, 1, -1, 0); 750 break; 751 } 752 } 753 } 754 } 755 } 756