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
17package com.android.server.power;
18
19import java.io.PrintWriter;
20import java.nio.ByteBuffer;
21import java.nio.ByteOrder;
22import java.nio.FloatBuffer;
23
24import android.graphics.PixelFormat;
25import android.graphics.SurfaceTexture;
26import android.opengl.EGL14;
27import android.opengl.EGLConfig;
28import android.opengl.EGLContext;
29import android.opengl.EGLDisplay;
30import android.opengl.EGLSurface;
31import android.opengl.GLES10;
32import android.opengl.GLES11Ext;
33import android.os.Looper;
34import android.util.FloatMath;
35import android.util.Slog;
36import android.view.Display;
37import android.view.DisplayInfo;
38import android.view.Surface.OutOfResourcesException;
39import android.view.Surface;
40import android.view.SurfaceControl;
41import android.view.SurfaceSession;
42
43import com.android.server.display.DisplayManagerService;
44import 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 */
56final 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