blob: 8e19e114502979d7a4d1f231eb9f041215c84096 [file] [log] [blame]
Jeff Brown96307042012-07-27 15:51:34 -07001/*
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
Jeff Brown7f3994e2012-12-04 14:04:28 -080019import com.android.server.display.DisplayManagerService;
20import com.android.server.display.DisplayTransactionListener;
21
Jeff Brown96307042012-07-27 15:51:34 -070022import android.graphics.Bitmap;
23import android.graphics.PixelFormat;
24import android.opengl.EGL14;
25import android.opengl.EGLConfig;
26import android.opengl.EGLContext;
27import android.opengl.EGLDisplay;
28import android.opengl.EGLSurface;
29import android.opengl.GLES10;
30import android.opengl.GLUtils;
31import android.os.Looper;
Jeff Brown96307042012-07-27 15:51:34 -070032import android.util.FloatMath;
33import android.util.Slog;
34import android.view.Display;
35import android.view.DisplayInfo;
36import android.view.Surface;
37import android.view.SurfaceSession;
Jeff Brown96307042012-07-27 15:51:34 -070038
39import java.io.PrintWriter;
40import java.nio.ByteBuffer;
41import java.nio.ByteOrder;
42import java.nio.FloatBuffer;
43
44/**
45 * Bzzzoooop! *crackle*
Jeff Brown8b9cf1c2012-10-07 14:54:17 -070046 * <p>
Jeff Brown96307042012-07-27 15:51:34 -070047 * Animates a screen transition from on to off or off to on by applying
48 * some GL transformations to a screenshot.
Jeff Brown8b9cf1c2012-10-07 14:54:17 -070049 * </p><p>
Jeff Brown96307042012-07-27 15:51:34 -070050 * This component must only be created or accessed by the {@link Looper} thread
51 * that belongs to the {@link DisplayPowerController}.
Jeff Brown8b9cf1c2012-10-07 14:54:17 -070052 * </p>
Jeff Brown96307042012-07-27 15:51:34 -070053 */
54final 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.
Jeff Brown3c584f22012-10-10 14:36:00 -070066 private static final float HSTRETCH_DURATION = 0.5f;
Jeff Brown96307042012-07-27 15:51:34 -070067 private static final float VSTRETCH_DURATION = 1.0f - HSTRETCH_DURATION;
68
Jeff Brown78eb1222012-10-10 18:27:44 -070069 // 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
Jeff Brown96307042012-07-27 15:51:34 -070074 // Set to true when the animation context has been fully prepared.
75 private boolean mPrepared;
Jeff Brown8b9cf1c2012-10-07 14:54:17 -070076 private int mMode;
Jeff Brown96307042012-07-27 15:51:34 -070077
Jeff Brown7f3994e2012-12-04 14:04:28 -080078 private final DisplayManagerService mDisplayManager;
Jeff Brown96307042012-07-27 15:51:34 -070079 private int mDisplayLayerStack; // layer stack associated with primary display
Jeff Brown96307042012-07-27 15:51:34 -070080 private int mDisplayWidth; // real width, not rotated
81 private int mDisplayHeight; // real height, not rotated
82 private SurfaceSession mSurfaceSession;
83 private Surface mSurface;
Jeff Brown7f3994e2012-12-04 14:04:28 -080084 private NaturalSurfaceLayout mSurfaceLayout;
Jeff Brown96307042012-07-27 15:51:34 -070085 private EGLDisplay mEglDisplay;
86 private EGLConfig mEglConfig;
87 private EGLContext mEglContext;
88 private EGLSurface mEglSurface;
89 private boolean mSurfaceVisible;
Jeff Brown252c2062012-10-08 16:21:01 -070090 private float mSurfaceAlpha;
Jeff Brown96307042012-07-27 15:51:34 -070091
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
Jeff Brown252c2062012-10-08 16:21:01 -0700101 /**
102 * Animates an electron beam warming up.
103 */
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700104 public static final int MODE_WARM_UP = 0;
Jeff Brown252c2062012-10-08 16:21:01 -0700105
106 /**
107 * Animates an electron beam shutting off.
108 */
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700109 public static final int MODE_COOL_DOWN = 1;
Jeff Brown252c2062012-10-08 16:21:01 -0700110
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;
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700115
Jeff Brown7f3994e2012-12-04 14:04:28 -0800116 public ElectronBeam(DisplayManagerService displayManager) {
117 mDisplayManager = displayManager;
Jeff Brown96307042012-07-27 15:51:34 -0700118 }
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 *
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700124 * @param mode The desired mode for the upcoming animation.
Jeff Brown96307042012-07-27 15:51:34 -0700125 * @return True if the electron beam is ready, false if it is uncontrollable.
126 */
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700127 public boolean prepare(int mode) {
Jeff Brown96307042012-07-27 15:51:34 -0700128 if (DEBUG) {
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700129 Slog.d(TAG, "prepare: mode=" + mode);
Jeff Brown96307042012-07-27 15:51:34 -0700130 }
131
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700132 mMode = mode;
Jeff Brown96307042012-07-27 15:51:34 -0700133
Jeff Brown7f3994e2012-12-04 14:04:28 -0800134 // 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();
Jeff Brown96307042012-07-27 15:51:34 -0700140
141 // Prepare the surface for drawing.
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700142 if (!tryPrepare()) {
Jeff Brown96307042012-07-27 15:51:34 -0700143 dismiss();
144 return false;
145 }
146
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700147 // Done.
Jeff Brown96307042012-07-27 15:51:34 -0700148 mPrepared = true;
Jeff Brown78eb1222012-10-10 18:27:44 -0700149
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 }
Jeff Brown96307042012-07-27 15:51:34 -0700162 return true;
163 }
164
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700165 private boolean tryPrepare() {
166 if (createSurface()) {
Jeff Brown252c2062012-10-08 16:21:01 -0700167 if (mMode == MODE_FADE) {
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700168 return true;
169 }
170 return createEglContext()
171 && createEglSurface()
172 && captureScreenshotTextureAndSetViewport();
173 }
174 return false;
175 }
176
Jeff Brown96307042012-07-27 15:51:34 -0700177 /**
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 */
184 public void dismiss() {
185 if (DEBUG) {
186 Slog.d(TAG, "dismiss");
187 }
188
189 destroyScreenshotTexture();
190 destroyEglSurface();
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700191 destroySurface();
Jeff Brown96307042012-07-27 15:51:34 -0700192 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 */
202 public boolean draw(float level) {
203 if (DEBUG) {
204 Slog.d(TAG, "drawFrame: level=" + level);
205 }
206
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700207 if (!mPrepared) {
208 return false;
209 }
210
Jeff Brown252c2062012-10-08 16:21:01 -0700211 if (mMode == MODE_FADE) {
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700212 return showSurface(1.0f - level);
213 }
214
Jeff Brown96307042012-07-27 15:51:34 -0700215 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 }
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700237 return showSurface(1.0f);
Jeff Brown96307042012-07-27 15:51:34 -0700238 }
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 */
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,
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700268 mMode == MODE_WARM_UP ? GLES10.GL_MODULATE : GLES10.GL_REPLACE);
Jeff Brown96307042012-07-27 15:51:34 -0700269 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)
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700302 if (mMode == MODE_COOL_DOWN) {
Jeff Brown96307042012-07-27 15:51:34 -0700303 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 */
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
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
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
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
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
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
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
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700523 private boolean createSurface() {
Jeff Brown96307042012-07-27 15:51:34 -0700524 if (mSurfaceSession == null) {
525 mSurfaceSession = new SurfaceSession();
526 }
527
528 Surface.openTransaction();
529 try {
530 if (mSurface == null) {
531 try {
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700532 int flags;
Jeff Brown252c2062012-10-08 16:21:01 -0700533 if (mMode == MODE_FADE) {
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700534 flags = Surface.FX_SURFACE_DIM | Surface.HIDDEN;
535 } else {
536 flags = Surface.OPAQUE | Surface.HIDDEN;
537 }
Jeff Brown64a55af2012-08-26 02:47:39 -0700538 mSurface = new Surface(mSurfaceSession,
539 "ElectronBeam", mDisplayWidth, mDisplayHeight,
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700540 PixelFormat.OPAQUE, flags);
Jeff Brown96307042012-07-27 15:51:34 -0700541 } catch (Surface.OutOfResourcesException ex) {
542 Slog.e(TAG, "Unable to create surface.", ex);
543 return false;
544 }
545 }
546
Jeff Brown64a55af2012-08-26 02:47:39 -0700547 mSurface.setLayerStack(mDisplayLayerStack);
Jeff Brown96307042012-07-27 15:51:34 -0700548 mSurface.setSize(mDisplayWidth, mDisplayHeight);
549
Jeff Brown7f3994e2012-12-04 14:04:28 -0800550 mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManager, mSurface);
551 mSurfaceLayout.onDisplayTransaction();
Jeff Brown96307042012-07-27 15:51:34 -0700552 } finally {
553 Surface.closeTransaction();
554 }
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700555 return true;
556 }
Jeff Brown96307042012-07-27 15:51:34 -0700557
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700558 private boolean createEglSurface() {
Jeff Brown96307042012-07-27 15:51:34 -0700559 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
573 private void destroyEglSurface() {
574 if (mEglSurface != null) {
575 if (!EGL14.eglDestroySurface(mEglDisplay, mEglSurface)) {
576 logEglError("eglDestroySurface");
577 }
578 mEglSurface = null;
579 }
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700580 }
Jeff Brown96307042012-07-27 15:51:34 -0700581
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700582 private void destroySurface() {
Jeff Brown96307042012-07-27 15:51:34 -0700583 if (mSurface != null) {
Jeff Brown7f3994e2012-12-04 14:04:28 -0800584 mSurfaceLayout.dispose();
585 mSurfaceLayout = null;
Jeff Brown96307042012-07-27 15:51:34 -0700586 Surface.openTransaction();
587 try {
588 mSurface.destroy();
589 } finally {
590 Surface.closeTransaction();
591 }
592 mSurface = null;
593 mSurfaceVisible = false;
Jeff Brown252c2062012-10-08 16:21:01 -0700594 mSurfaceAlpha = 0f;
Jeff Brown96307042012-07-27 15:51:34 -0700595 }
596 }
597
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700598 private boolean showSurface(float alpha) {
Jeff Brown252c2062012-10-08 16:21:01 -0700599 if (!mSurfaceVisible || mSurfaceAlpha != alpha) {
Jeff Brown96307042012-07-27 15:51:34 -0700600 Surface.openTransaction();
601 try {
602 mSurface.setLayer(ELECTRON_BEAM_LAYER);
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700603 mSurface.setAlpha(alpha);
Jeff Brown96307042012-07-27 15:51:34 -0700604 mSurface.show();
605 } finally {
606 Surface.closeTransaction();
607 }
608 mSurfaceVisible = true;
Jeff Brown252c2062012-10-08 16:21:01 -0700609 mSurfaceAlpha = alpha;
Jeff Brown96307042012-07-27 15:51:34 -0700610 }
611 return true;
612 }
613
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
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 */
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
656 private static float sigmoid(float x, float s) {
657 return 1.0f / (1.0f + FloatMath.exp(-x * s));
658 }
659
660 private static int nextPowerOfTwo(int value) {
661 return 1 << (32 - Integer.numberOfLeadingZeros(value));
662 }
663
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
670 private static void logEglError(String func) {
671 Slog.e(TAG, func + " failed: error " + EGL14.eglGetError(), new Throwable());
672 }
673
674 private static boolean checkGlErrors(String func) {
675 return checkGlErrors(func, true);
676 }
677
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
690 public void dump(PrintWriter pw) {
691 pw.println();
692 pw.println("Electron Beam State:");
693 pw.println(" mPrepared=" + mPrepared);
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700694 pw.println(" mMode=" + mMode);
Jeff Brown96307042012-07-27 15:51:34 -0700695 pw.println(" mDisplayLayerStack=" + mDisplayLayerStack);
Jeff Brown96307042012-07-27 15:51:34 -0700696 pw.println(" mDisplayWidth=" + mDisplayWidth);
697 pw.println(" mDisplayHeight=" + mDisplayHeight);
698 pw.println(" mSurfaceVisible=" + mSurfaceVisible);
Jeff Brown252c2062012-10-08 16:21:01 -0700699 pw.println(" mSurfaceAlpha=" + mSurfaceAlpha);
Jeff Brown96307042012-07-27 15:51:34 -0700700 }
Jeff Brown7f3994e2012-12-04 14:04:28 -0800701
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
713 public NaturalSurfaceLayout(DisplayManagerService displayManager, Surface surface) {
714 mDisplayManager = displayManager;
715 mSurface = surface;
716 mDisplayManager.registerDisplayTransactionListener(this);
717 }
718
719 public void dispose() {
720 synchronized (this) {
721 mSurface = null;
722 }
723 mDisplayManager.unregisterDisplayTransactionListener(this);
724 }
725
726 @Override
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 }
Jeff Brown96307042012-07-27 15:51:34 -0700755}