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