Now using about the same duration as it used to have when it was implemented in Surface Flinger. Change-Id: I09b28222379f4b6583f1f7b34188cc36c50509e9
653 lines
23 KiB
Java
653 lines
23 KiB
Java
/*
|
|
* Copyright (C) 2012 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.server.power;
|
|
|
|
import android.graphics.Bitmap;
|
|
import android.graphics.PixelFormat;
|
|
import android.opengl.EGL14;
|
|
import android.opengl.EGLConfig;
|
|
import android.opengl.EGLContext;
|
|
import android.opengl.EGLDisplay;
|
|
import android.opengl.EGLSurface;
|
|
import android.opengl.GLES10;
|
|
import android.opengl.GLUtils;
|
|
import android.os.Looper;
|
|
import android.os.Process;
|
|
import android.util.FloatMath;
|
|
import android.util.Slog;
|
|
import android.view.Display;
|
|
import android.view.DisplayInfo;
|
|
import android.view.Surface;
|
|
import android.view.SurfaceSession;
|
|
|
|
import java.io.PrintWriter;
|
|
import java.nio.ByteBuffer;
|
|
import java.nio.ByteOrder;
|
|
import java.nio.FloatBuffer;
|
|
|
|
/**
|
|
* Bzzzoooop! *crackle*
|
|
*
|
|
* Animates a screen transition from on to off or off to on by applying
|
|
* some GL transformations to a screenshot.
|
|
*
|
|
* This component must only be created or accessed by the {@link Looper} thread
|
|
* that belongs to the {@link DisplayPowerController}.
|
|
*/
|
|
final class ElectronBeam {
|
|
private static final String TAG = "ElectronBeam";
|
|
|
|
private static final boolean DEBUG = false;
|
|
|
|
// The layer for the electron beam surface.
|
|
// This is currently hardcoded to be one layer above the boot animation.
|
|
private static final int ELECTRON_BEAM_LAYER = 0x40000001;
|
|
|
|
// The relative proportion of the animation to spend performing
|
|
// the horizontal stretch effect. The remainder is spent performing
|
|
// the vertical stretch effect.
|
|
private static final float HSTRETCH_DURATION = 0.4f;
|
|
private static final float VSTRETCH_DURATION = 1.0f - HSTRETCH_DURATION;
|
|
|
|
// Set to true when the animation context has been fully prepared.
|
|
private boolean mPrepared;
|
|
private boolean mWarmUp;
|
|
|
|
private final Display mDisplay;
|
|
private final DisplayInfo mDisplayInfo = new DisplayInfo();
|
|
private int mDisplayLayerStack; // layer stack associated with primary display
|
|
private int mDisplayRotation;
|
|
private int mDisplayWidth; // real width, not rotated
|
|
private int mDisplayHeight; // real height, not rotated
|
|
private SurfaceSession mSurfaceSession;
|
|
private Surface mSurface;
|
|
private EGLDisplay mEglDisplay;
|
|
private EGLConfig mEglConfig;
|
|
private EGLContext mEglContext;
|
|
private EGLSurface mEglSurface;
|
|
private boolean mSurfaceVisible;
|
|
|
|
// Texture names. We only use one texture, which contains the screenshot.
|
|
private final int[] mTexNames = new int[1];
|
|
private boolean mTexNamesGenerated;
|
|
|
|
// Vertex and corresponding texture coordinates.
|
|
// We have 4 2D vertices, so 8 elements. The vertices form a quad.
|
|
private final FloatBuffer mVertexBuffer = createNativeFloatBuffer(8);
|
|
private final FloatBuffer mTexCoordBuffer = createNativeFloatBuffer(8);
|
|
|
|
public ElectronBeam(Display display) {
|
|
mDisplay = display;
|
|
}
|
|
|
|
/**
|
|
* Warms up the electron beam in preparation for turning on or off.
|
|
* This method prepares a GL context, and captures a screen shot.
|
|
*
|
|
* @param warmUp True if the electron beam is about to be turned on, false if
|
|
* it is about to be turned off.
|
|
* @return True if the electron beam is ready, false if it is uncontrollable.
|
|
*/
|
|
public boolean prepare(boolean warmUp) {
|
|
if (DEBUG) {
|
|
Slog.d(TAG, "prepare: warmUp=" + warmUp);
|
|
}
|
|
|
|
mWarmUp = warmUp;
|
|
|
|
// Get the display size and adjust it for rotation.
|
|
mDisplay.getDisplayInfo(mDisplayInfo);
|
|
mDisplayLayerStack = mDisplay.getLayerStack();
|
|
mDisplayRotation = mDisplayInfo.rotation;
|
|
if (mDisplayRotation == Surface.ROTATION_90
|
|
|| mDisplayRotation == Surface.ROTATION_270) {
|
|
mDisplayWidth = mDisplayInfo.logicalHeight;
|
|
mDisplayHeight = mDisplayInfo.logicalWidth;
|
|
} else {
|
|
mDisplayWidth = mDisplayInfo.logicalWidth;
|
|
mDisplayHeight = mDisplayInfo.logicalHeight;
|
|
}
|
|
|
|
// Prepare the surface for drawing.
|
|
if (!createEglContext()
|
|
|| !createEglSurface()
|
|
|| !captureScreenshotTextureAndSetViewport()) {
|
|
dismiss();
|
|
return false;
|
|
}
|
|
|
|
mPrepared = true;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Dismisses the electron beam animation surface and cleans up.
|
|
*
|
|
* To prevent stray photons from leaking out after the electron beam has been
|
|
* turned off, it is a good idea to defer dismissing the animation until the
|
|
* electron beam has been turned back on fully.
|
|
*/
|
|
public void dismiss() {
|
|
if (DEBUG) {
|
|
Slog.d(TAG, "dismiss");
|
|
}
|
|
|
|
destroyScreenshotTexture();
|
|
destroyEglSurface();
|
|
mPrepared = false;
|
|
}
|
|
|
|
/**
|
|
* Draws an animation frame showing the electron beam activated at the
|
|
* specified level.
|
|
*
|
|
* @param level The electron beam level.
|
|
* @return True if successful.
|
|
*/
|
|
public boolean draw(float level) {
|
|
if (DEBUG) {
|
|
Slog.d(TAG, "drawFrame: level=" + level);
|
|
}
|
|
|
|
if (!attachEglContext()) {
|
|
return false;
|
|
}
|
|
try {
|
|
// Clear frame to solid black.
|
|
GLES10.glClearColor(0f, 0f, 0f, 1f);
|
|
GLES10.glClear(GLES10.GL_COLOR_BUFFER_BIT);
|
|
|
|
// Draw the frame.
|
|
if (level < HSTRETCH_DURATION) {
|
|
drawHStretch(1.0f - (level / HSTRETCH_DURATION));
|
|
} else {
|
|
drawVStretch(1.0f - ((level - HSTRETCH_DURATION) / VSTRETCH_DURATION));
|
|
}
|
|
if (checkGlErrors("drawFrame")) {
|
|
return false;
|
|
}
|
|
|
|
EGL14.eglSwapBuffers(mEglDisplay, mEglSurface);
|
|
} finally {
|
|
detachEglContext();
|
|
}
|
|
|
|
return showEglSurface();
|
|
}
|
|
|
|
/**
|
|
* Draws a frame where the content of the electron beam is collapsing inwards upon
|
|
* itself vertically with red / green / blue channels dispersing and eventually
|
|
* merging down to a single horizontal line.
|
|
*
|
|
* @param stretch The stretch factor. 0.0 is no collapse, 1.0 is full collapse.
|
|
*/
|
|
private void drawVStretch(float stretch) {
|
|
// compute interpolation scale factors for each color channel
|
|
final float ar = scurve(stretch, 7.5f);
|
|
final float ag = scurve(stretch, 8.0f);
|
|
final float ab = scurve(stretch, 8.5f);
|
|
if (DEBUG) {
|
|
Slog.d(TAG, "drawVStretch: stretch=" + stretch
|
|
+ ", ar=" + ar + ", ag=" + ag + ", ab=" + ab);
|
|
}
|
|
|
|
// set blending
|
|
GLES10.glBlendFunc(GLES10.GL_ONE, GLES10.GL_ONE);
|
|
GLES10.glEnable(GLES10.GL_BLEND);
|
|
|
|
// bind vertex buffer
|
|
GLES10.glVertexPointer(2, GLES10.GL_FLOAT, 0, mVertexBuffer);
|
|
GLES10.glEnableClientState(GLES10.GL_VERTEX_ARRAY);
|
|
|
|
// bind texture and set blending for drawing planes
|
|
GLES10.glBindTexture(GLES10.GL_TEXTURE_2D, mTexNames[0]);
|
|
GLES10.glTexEnvx(GLES10.GL_TEXTURE_ENV, GLES10.GL_TEXTURE_ENV_MODE,
|
|
mWarmUp ? GLES10.GL_MODULATE : GLES10.GL_REPLACE);
|
|
GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
|
|
GLES10.GL_TEXTURE_MAG_FILTER, GLES10.GL_LINEAR);
|
|
GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
|
|
GLES10.GL_TEXTURE_MIN_FILTER, GLES10.GL_LINEAR);
|
|
GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
|
|
GLES10.GL_TEXTURE_WRAP_S, GLES10.GL_CLAMP_TO_EDGE);
|
|
GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
|
|
GLES10.GL_TEXTURE_WRAP_T, GLES10.GL_CLAMP_TO_EDGE);
|
|
GLES10.glEnable(GLES10.GL_TEXTURE_2D);
|
|
GLES10.glTexCoordPointer(2, GLES10.GL_FLOAT, 0, mTexCoordBuffer);
|
|
GLES10.glEnableClientState(GLES10.GL_TEXTURE_COORD_ARRAY);
|
|
|
|
// draw the red plane
|
|
setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ar);
|
|
GLES10.glColorMask(true, false, false, true);
|
|
GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
|
|
|
|
// draw the green plane
|
|
setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ag);
|
|
GLES10.glColorMask(false, true, false, true);
|
|
GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
|
|
|
|
// draw the blue plane
|
|
setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ab);
|
|
GLES10.glColorMask(false, false, true, true);
|
|
GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
|
|
|
|
// clean up after drawing planes
|
|
GLES10.glDisable(GLES10.GL_TEXTURE_2D);
|
|
GLES10.glDisableClientState(GLES10.GL_TEXTURE_COORD_ARRAY);
|
|
GLES10.glColorMask(true, true, true, true);
|
|
|
|
// draw the white highlight (we use the last vertices)
|
|
if (!mWarmUp) {
|
|
GLES10.glColor4f(ag, ag, ag, 1.0f);
|
|
GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
|
|
}
|
|
|
|
// clean up
|
|
GLES10.glDisableClientState(GLES10.GL_VERTEX_ARRAY);
|
|
GLES10.glDisable(GLES10.GL_BLEND);
|
|
}
|
|
|
|
/**
|
|
* Draws a frame where the electron beam has been stretched out into
|
|
* a thin white horizontal line that fades as it expands outwards.
|
|
*
|
|
* @param stretch The stretch factor. 0.0 is no stretch / no fade,
|
|
* 1.0 is maximum stretch / maximum fade.
|
|
*/
|
|
private void drawHStretch(float stretch) {
|
|
// compute interpolation scale factor
|
|
final float ag = scurve(stretch, 8.0f);
|
|
if (DEBUG) {
|
|
Slog.d(TAG, "drawHStretch: stretch=" + stretch + ", ag=" + ag);
|
|
}
|
|
|
|
if (stretch < 1.0f) {
|
|
// bind vertex buffer
|
|
GLES10.glVertexPointer(2, GLES10.GL_FLOAT, 0, mVertexBuffer);
|
|
GLES10.glEnableClientState(GLES10.GL_VERTEX_ARRAY);
|
|
|
|
// draw narrow fading white line
|
|
setHStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ag);
|
|
GLES10.glColor4f(1.0f - ag, 1.0f - ag, 1.0f - ag, 1.0f);
|
|
GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
|
|
|
|
// clean up
|
|
GLES10.glDisableClientState(GLES10.GL_VERTEX_ARRAY);
|
|
}
|
|
}
|
|
|
|
private static void setVStretchQuad(FloatBuffer vtx, float dw, float dh, float a) {
|
|
final float w = dw + (dw * a);
|
|
final float h = dh - (dh * a);
|
|
final float x = (dw - w) * 0.5f;
|
|
final float y = (dh - h) * 0.5f;
|
|
setQuad(vtx, x, y, w, h);
|
|
}
|
|
|
|
private static void setHStretchQuad(FloatBuffer vtx, float dw, float dh, float a) {
|
|
final float w = dw + (dw * a);
|
|
final float h = 1.0f;
|
|
final float x = (dw - w) * 0.5f;
|
|
final float y = (dh - h) * 0.5f;
|
|
setQuad(vtx, x, y, w, h);
|
|
}
|
|
|
|
private static void setQuad(FloatBuffer vtx, float x, float y, float w, float h) {
|
|
if (DEBUG) {
|
|
Slog.d(TAG, "setQuad: x=" + x + ", y=" + y + ", w=" + w + ", h=" + h);
|
|
}
|
|
vtx.put(0, x);
|
|
vtx.put(1, y);
|
|
vtx.put(2, x);
|
|
vtx.put(3, y + h);
|
|
vtx.put(4, x + w);
|
|
vtx.put(5, y + h);
|
|
vtx.put(6, x + w);
|
|
vtx.put(7, y);
|
|
}
|
|
|
|
private boolean captureScreenshotTextureAndSetViewport() {
|
|
// TODO: Use a SurfaceTexture to avoid the extra texture upload.
|
|
Bitmap bitmap = Surface.screenshot(mDisplayWidth, mDisplayHeight,
|
|
0, ELECTRON_BEAM_LAYER - 1);
|
|
if (bitmap == null) {
|
|
Slog.e(TAG, "Could not take a screenshot!");
|
|
return false;
|
|
}
|
|
try {
|
|
if (!attachEglContext()) {
|
|
return false;
|
|
}
|
|
try {
|
|
if (!mTexNamesGenerated) {
|
|
GLES10.glGenTextures(1, mTexNames, 0);
|
|
if (checkGlErrors("glGenTextures")) {
|
|
return false;
|
|
}
|
|
mTexNamesGenerated = true;
|
|
}
|
|
|
|
GLES10.glBindTexture(GLES10.GL_TEXTURE_2D, mTexNames[0]);
|
|
if (checkGlErrors("glBindTexture")) {
|
|
return false;
|
|
}
|
|
|
|
float u = 1.0f;
|
|
float v = 1.0f;
|
|
GLUtils.texImage2D(GLES10.GL_TEXTURE_2D, 0, bitmap, 0);
|
|
if (checkGlErrors("glTexImage2D, first try", false)) {
|
|
// Try a power of two size texture instead.
|
|
int tw = nextPowerOfTwo(mDisplayWidth);
|
|
int th = nextPowerOfTwo(mDisplayHeight);
|
|
int format = GLUtils.getInternalFormat(bitmap);
|
|
GLES10.glTexImage2D(GLES10.GL_TEXTURE_2D, 0,
|
|
format, tw, th, 0,
|
|
format, GLES10.GL_UNSIGNED_BYTE, null);
|
|
if (checkGlErrors("glTexImage2D, second try")) {
|
|
return false;
|
|
}
|
|
|
|
GLUtils.texSubImage2D(GLES10.GL_TEXTURE_2D, 0, 0, 0, bitmap);
|
|
if (checkGlErrors("glTexSubImage2D")) {
|
|
return false;
|
|
}
|
|
|
|
u = (float)mDisplayWidth / tw;
|
|
v = (float)mDisplayHeight / th;
|
|
}
|
|
|
|
// Set up texture coordinates for a quad.
|
|
// We might need to change this if the texture ends up being
|
|
// a different size from the display for some reason.
|
|
mTexCoordBuffer.put(0, 0f);
|
|
mTexCoordBuffer.put(1, v);
|
|
mTexCoordBuffer.put(2, 0f);
|
|
mTexCoordBuffer.put(3, 0f);
|
|
mTexCoordBuffer.put(4, u);
|
|
mTexCoordBuffer.put(5, 0f);
|
|
mTexCoordBuffer.put(6, u);
|
|
mTexCoordBuffer.put(7, v);
|
|
|
|
// Set up our viewport.
|
|
GLES10.glViewport(0, 0, mDisplayWidth, mDisplayHeight);
|
|
GLES10.glMatrixMode(GLES10.GL_PROJECTION);
|
|
GLES10.glLoadIdentity();
|
|
GLES10.glOrthof(0, mDisplayWidth, 0, mDisplayHeight, 0, 1);
|
|
GLES10.glMatrixMode(GLES10.GL_MODELVIEW);
|
|
GLES10.glLoadIdentity();
|
|
GLES10.glMatrixMode(GLES10.GL_TEXTURE);
|
|
GLES10.glLoadIdentity();
|
|
} finally {
|
|
detachEglContext();
|
|
}
|
|
} finally {
|
|
bitmap.recycle();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private void destroyScreenshotTexture() {
|
|
if (mTexNamesGenerated) {
|
|
mTexNamesGenerated = false;
|
|
if (attachEglContext()) {
|
|
try {
|
|
GLES10.glDeleteTextures(1, mTexNames, 0);
|
|
checkGlErrors("glDeleteTextures");
|
|
} finally {
|
|
detachEglContext();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean createEglContext() {
|
|
if (mEglDisplay == null) {
|
|
mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
|
|
if (mEglDisplay == EGL14.EGL_NO_DISPLAY) {
|
|
logEglError("eglGetDisplay");
|
|
return false;
|
|
}
|
|
|
|
int[] version = new int[2];
|
|
if (!EGL14.eglInitialize(mEglDisplay, version, 0, version, 1)) {
|
|
mEglDisplay = null;
|
|
logEglError("eglInitialize");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (mEglConfig == null) {
|
|
int[] eglConfigAttribList = new int[] {
|
|
EGL14.EGL_RED_SIZE, 8,
|
|
EGL14.EGL_GREEN_SIZE, 8,
|
|
EGL14.EGL_BLUE_SIZE, 8,
|
|
EGL14.EGL_ALPHA_SIZE, 8,
|
|
EGL14.EGL_NONE
|
|
};
|
|
int[] numEglConfigs = new int[1];
|
|
EGLConfig[] eglConfigs = new EGLConfig[1];
|
|
if (!EGL14.eglChooseConfig(mEglDisplay, eglConfigAttribList, 0,
|
|
eglConfigs, 0, eglConfigs.length, numEglConfigs, 0)) {
|
|
logEglError("eglChooseConfig");
|
|
return false;
|
|
}
|
|
mEglConfig = eglConfigs[0];
|
|
}
|
|
|
|
if (mEglContext == null) {
|
|
int[] eglContextAttribList = new int[] {
|
|
EGL14.EGL_NONE
|
|
};
|
|
mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig,
|
|
EGL14.EGL_NO_CONTEXT, eglContextAttribList, 0);
|
|
if (mEglContext == null) {
|
|
logEglError("eglCreateContext");
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* not used because it is too expensive to create / destroy contexts all of the time
|
|
private void destroyEglContext() {
|
|
if (mEglContext != null) {
|
|
if (!EGL14.eglDestroyContext(mEglDisplay, mEglContext)) {
|
|
logEglError("eglDestroyContext");
|
|
}
|
|
mEglContext = null;
|
|
}
|
|
}*/
|
|
|
|
private boolean createEglSurface() {
|
|
if (mSurfaceSession == null) {
|
|
mSurfaceSession = new SurfaceSession();
|
|
}
|
|
|
|
Surface.openTransaction();
|
|
try {
|
|
if (mSurface == null) {
|
|
try {
|
|
mSurface = new Surface(mSurfaceSession, Process.myPid(),
|
|
"ElectronBeam", mDisplayLayerStack, mDisplayWidth, mDisplayHeight,
|
|
PixelFormat.OPAQUE, Surface.OPAQUE | Surface.HIDDEN);
|
|
} catch (Surface.OutOfResourcesException ex) {
|
|
Slog.e(TAG, "Unable to create surface.", ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
mSurface.setSize(mDisplayWidth, mDisplayHeight);
|
|
|
|
switch (mDisplayRotation) {
|
|
case Surface.ROTATION_0:
|
|
mSurface.setPosition(0, 0);
|
|
mSurface.setMatrix(1, 0, 0, 1);
|
|
break;
|
|
case Surface.ROTATION_90:
|
|
mSurface.setPosition(0, mDisplayWidth);
|
|
mSurface.setMatrix(0, -1, 1, 0);
|
|
break;
|
|
case Surface.ROTATION_180:
|
|
mSurface.setPosition(mDisplayWidth, mDisplayHeight);
|
|
mSurface.setMatrix(-1, 0, 0, -1);
|
|
break;
|
|
case Surface.ROTATION_270:
|
|
mSurface.setPosition(mDisplayHeight, 0);
|
|
mSurface.setMatrix(0, 1, -1, 0);
|
|
break;
|
|
}
|
|
} finally {
|
|
Surface.closeTransaction();
|
|
}
|
|
|
|
if (mEglSurface == null) {
|
|
int[] eglSurfaceAttribList = new int[] {
|
|
EGL14.EGL_NONE
|
|
};
|
|
mEglSurface = EGL14.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface,
|
|
eglSurfaceAttribList, 0);
|
|
if (mEglSurface == null) {
|
|
logEglError("eglCreateWindowSurface");
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private void destroyEglSurface() {
|
|
if (mEglSurface != null) {
|
|
if (!EGL14.eglDestroySurface(mEglDisplay, mEglSurface)) {
|
|
logEglError("eglDestroySurface");
|
|
}
|
|
mEglSurface = null;
|
|
}
|
|
|
|
if (mSurface != null) {
|
|
Surface.openTransaction();
|
|
try {
|
|
mSurface.destroy();
|
|
} finally {
|
|
Surface.closeTransaction();
|
|
}
|
|
mSurface = null;
|
|
mSurfaceVisible = false;
|
|
}
|
|
}
|
|
|
|
private boolean showEglSurface() {
|
|
if (!mSurfaceVisible) {
|
|
Surface.openTransaction();
|
|
try {
|
|
mSurface.setLayer(ELECTRON_BEAM_LAYER);
|
|
mSurface.show();
|
|
} finally {
|
|
Surface.closeTransaction();
|
|
}
|
|
mSurfaceVisible = true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private boolean attachEglContext() {
|
|
if (mEglSurface == null) {
|
|
return false;
|
|
}
|
|
if (!EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
|
|
logEglError("eglMakeCurrent");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private void detachEglContext() {
|
|
if (mEglDisplay != null) {
|
|
EGL14.eglMakeCurrent(mEglDisplay,
|
|
EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Interpolates a value in the range 0 .. 1 along a sigmoid curve
|
|
* yielding a result in the range 0 .. 1 scaled such that:
|
|
* scurve(0) == 0, scurve(0.5) == 0.5, scurve(1) == 1.
|
|
*/
|
|
private static float scurve(float value, float s) {
|
|
// A basic sigmoid has the form y = 1.0f / FloatMap.exp(-x * s).
|
|
// Here we take the input datum and shift it by 0.5 so that the
|
|
// domain spans the range -0.5 .. 0.5 instead of 0 .. 1.
|
|
final float x = value - 0.5f;
|
|
|
|
// Next apply the sigmoid function to the scaled value
|
|
// which produces a value in the range 0 .. 1 so we subtract
|
|
// 0.5 to get a value in the range -0.5 .. 0.5 instead.
|
|
final float y = sigmoid(x, s) - 0.5f;
|
|
|
|
// To obtain the desired boundary conditions we need to scale
|
|
// the result so that it fills a range of -1 .. 1.
|
|
final float v = sigmoid(0.5f, s) - 0.5f;
|
|
|
|
// And finally remap the value back to a range of 0 .. 1.
|
|
return y / v * 0.5f + 0.5f;
|
|
}
|
|
|
|
private static float sigmoid(float x, float s) {
|
|
return 1.0f / (1.0f + FloatMath.exp(-x * s));
|
|
}
|
|
|
|
private static int nextPowerOfTwo(int value) {
|
|
return 1 << (32 - Integer.numberOfLeadingZeros(value));
|
|
}
|
|
|
|
private static FloatBuffer createNativeFloatBuffer(int size) {
|
|
ByteBuffer bb = ByteBuffer.allocateDirect(size * 4);
|
|
bb.order(ByteOrder.nativeOrder());
|
|
return bb.asFloatBuffer();
|
|
}
|
|
|
|
private static void logEglError(String func) {
|
|
Slog.e(TAG, func + " failed: error " + EGL14.eglGetError(), new Throwable());
|
|
}
|
|
|
|
private static boolean checkGlErrors(String func) {
|
|
return checkGlErrors(func, true);
|
|
}
|
|
|
|
private static boolean checkGlErrors(String func, boolean log) {
|
|
boolean hadError = false;
|
|
int error;
|
|
while ((error = GLES10.glGetError()) != GLES10.GL_NO_ERROR) {
|
|
if (log) {
|
|
Slog.e(TAG, func + " failed: error " + error, new Throwable());
|
|
}
|
|
hadError = true;
|
|
}
|
|
return hadError;
|
|
}
|
|
|
|
public void dump(PrintWriter pw) {
|
|
pw.println();
|
|
pw.println("Electron Beam State:");
|
|
pw.println(" mPrepared=" + mPrepared);
|
|
pw.println(" mWarmUp=" + mWarmUp);
|
|
pw.println(" mDisplayLayerStack=" + mDisplayLayerStack);
|
|
pw.println(" mDisplayRotation=" + mDisplayRotation);
|
|
pw.println(" mDisplayWidth=" + mDisplayWidth);
|
|
pw.println(" mDisplayHeight=" + mDisplayHeight);
|
|
pw.println(" mSurfaceVisible=" + mSurfaceVisible);
|
|
}
|
|
}
|