am 9302251e: Merge "Reduce screen on latency, eliminate flashes." into jb-mr1-dev

* commit '9302251e3a09810164895237a6a2e8ac4987c3c0':
  Reduce screen on latency, eliminate flashes.
This commit is contained in:
Jeff Brown
2012-10-07 23:35:51 -07:00
committed by Android Git Automerger
5 changed files with 116 additions and 70 deletions

View File

@@ -271,6 +271,12 @@ final class DisplayPowerController {
// When the screen turns on again, we report user activity to the power manager.
private boolean mScreenOffBecauseOfProximity;
// True if the screen on is being blocked.
private boolean mScreenOnWasBlocked;
// The elapsed real time when the screen on was blocked.
private long mScreenOnBlockStartRealTime;
// Set to true if the light sensor is enabled.
private boolean mLightSensorEnabled;
@@ -513,7 +519,7 @@ final class DisplayPowerController {
final Executor executor = AsyncTask.THREAD_POOL_EXECUTOR;
Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
mPowerState = new DisplayPowerState(
mElectronBeamAnimatesBacklightConfig ? null : new ElectronBeam(display),
new ElectronBeam(display),
new PhotonicModulator(executor,
mLights.getLight(LightsService.LIGHT_ID_BACKLIGHT),
mSuspendBlocker));
@@ -553,7 +559,6 @@ final class DisplayPowerController {
final boolean mustNotify;
boolean mustInitialize = false;
boolean updateAutoBrightness = mTwilightChanged;
boolean screenOnWasBlocked = false;
mTwilightChanged = false;
synchronized (mLock) {
@@ -662,18 +667,24 @@ final class DisplayPowerController {
// It is relatively short but if we cancel it and switch to the
// on animation immediately then the results are pretty ugly.
if (!mElectronBeamOffAnimator.isStarted()) {
if (mPowerRequest.blockScreenOn && !mPowerState.isScreenOn()) {
if (DEBUG) {
Slog.d(TAG, "Blocked screen on while screen currently off.");
}
screenOnWasBlocked = true;
// Turn the screen on. The contents of the screen may not yet
// be visible if the electron beam has not been dismissed because
// its last frame of animation is solid black.
setScreenOn(true);
if (mPowerRequest.blockScreenOn
&& mPowerState.getElectronBeamLevel() == 0.0f) {
blockScreenOn();
} else {
setScreenOn(true);
unblockScreenOn();
if (USE_ELECTRON_BEAM_ON_ANIMATION) {
if (!mElectronBeamOnAnimator.isStarted()) {
if (mPowerState.getElectronBeamLevel() == 1.0f) {
mPowerState.dismissElectronBeam();
} else if (mPowerState.prepareElectronBeam(true)) {
} else if (mPowerState.prepareElectronBeam(
mElectronBeamAnimatesBacklightConfig ?
ElectronBeam.MODE_BLANK :
ElectronBeam.MODE_WARM_UP)) {
mElectronBeamOnAnimator.start();
} else {
mElectronBeamOnAnimator.end();
@@ -684,22 +695,6 @@ final class DisplayPowerController {
mPowerState.dismissElectronBeam();
}
}
} else {
// FIXME: If the electron beam off animation is playing then we have a bit
// of a problem. The window manager policy would only have requested
// to block screen on if it was about to start preparing the keyguard.
// It's already too late to do anything about that. Ideally we would
// let the animation play out first but that would require making
// some pretty deep changes to the power manager and we don't have
// time just now. For now, short-circuit the animation and get ready.
if (mPowerRequest.blockScreenOn) {
if (DEBUG) {
Slog.d(TAG, "Blocked screen on while screen off animation running.");
}
screenOnWasBlocked = true;
setScreenOn(false);
mElectronBeamOffAnimator.end();
}
}
} else {
// Want screen off.
@@ -708,7 +703,10 @@ final class DisplayPowerController {
if (!mElectronBeamOffAnimator.isStarted()) {
if (mPowerState.getElectronBeamLevel() == 0.0f) {
setScreenOn(false);
} else if (mPowerState.prepareElectronBeam(false)
} else if (mPowerState.prepareElectronBeam(
mElectronBeamAnimatesBacklightConfig ?
ElectronBeam.MODE_BLANK :
ElectronBeam.MODE_COOL_DOWN)
&& mPowerState.isScreenOn()) {
mElectronBeamOffAnimator.start();
} else {
@@ -723,7 +721,7 @@ final class DisplayPowerController {
// We mostly care about the screen state here, ignoring brightness changes
// which will be handled asynchronously.
if (mustNotify
&& !screenOnWasBlocked
&& !mScreenOnWasBlocked
&& !mElectronBeamOnAnimator.isStarted()
&& !mElectronBeamOffAnimator.isStarted()
&& mPowerState.waitUntilClean(mCleanListener)) {
@@ -740,6 +738,26 @@ final class DisplayPowerController {
}
}
private void blockScreenOn() {
if (!mScreenOnWasBlocked) {
mScreenOnWasBlocked = true;
if (DEBUG) {
Slog.d(TAG, "Blocked screen on.");
mScreenOnBlockStartRealTime = SystemClock.elapsedRealtime();
}
}
}
private void unblockScreenOn() {
if (mScreenOnWasBlocked) {
mScreenOnWasBlocked = false;
if (DEBUG) {
Slog.d(TAG, "Unblocked screen on after " +
(SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime) + " ms");
}
}
}
private void setScreenOn(boolean on) {
if (!mPowerState.isScreenOn() == on) {
mPowerState.setScreenOn(on);

View File

@@ -52,11 +52,14 @@ final class DisplayPowerRequest {
// If true, enables automatic brightness control.
public boolean useAutoBrightness;
// If true, prevents the screen from turning on if it is currently off.
// The display does not enter a "ready" state if this flag is true and the screen
// is off and is being prevented from turning on. The window manager policy blocks
// screen on while it prepares the keyguard to prevent the user from seeing
// intermediate updates.
// If true, prevents the screen from completely turning on if it is currently off.
// The display does not enter a "ready" state if this flag is true and screen on is
// blocked. The window manager policy blocks screen on while it prepares the keyguard to
// prevent the user from seeing intermediate updates.
//
// Technically, we may not block the screen itself from turning on (because that introduces
// extra unnecessary latency) but we do prevent content on screen from becoming
// visible to the user.
public boolean blockScreenOn;
public DisplayPowerRequest() {

View File

@@ -50,7 +50,7 @@ final class DisplayPowerState {
private static final int DIRTY_BRIGHTNESS = 1 << 2;
private final Choreographer mChoreographer;
private final ElectronBeam mElectronBeam; // may be null if only animating backlights
private final ElectronBeam mElectronBeam;
private final PhotonicModulator mScreenBrightnessModulator;
private int mDirty;
@@ -130,26 +130,19 @@ final class DisplayPowerState {
* This method should be called before starting an animation because it
* can take a fair amount of time to prepare the electron beam surface.
*
* @param warmUp True if the electron beam should start warming up.
* @param mode The electron beam animation mode to prepare.
* @return True if the electron beam was prepared.
*/
public boolean prepareElectronBeam(boolean warmUp) {
if (mElectronBeam != null) {
boolean success = mElectronBeam.prepare(warmUp);
invalidate(DIRTY_ELECTRON_BEAM);
return success;
} else {
return true;
}
public boolean prepareElectronBeam(int mode) {
invalidate(DIRTY_ELECTRON_BEAM);
return mElectronBeam.prepare(mode);
}
/**
* Dismisses the electron beam surface.
*/
public void dismissElectronBeam() {
if (mElectronBeam != null) {
mElectronBeam.dismiss();
}
mElectronBeam.dismiss();
}
/**
@@ -230,9 +223,7 @@ final class DisplayPowerState {
pw.println(" mScreenBrightness=" + mScreenBrightness);
pw.println(" mElectronBeamLevel=" + mElectronBeamLevel);
if (mElectronBeam != null) {
mElectronBeam.dump(pw);
}
mElectronBeam.dump(pw);
}
private void invalidate(int dirty) {
@@ -251,7 +242,7 @@ final class DisplayPowerState {
PowerManagerService.nativeSetScreenState(false);
}
if ((mDirty & DIRTY_ELECTRON_BEAM) != 0 && mElectronBeam != null) {
if ((mDirty & DIRTY_ELECTRON_BEAM) != 0) {
mElectronBeam.draw(mElectronBeamLevel);
}

View File

@@ -26,7 +26,6 @@ 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;
@@ -41,12 +40,13 @@ import java.nio.FloatBuffer;
/**
* Bzzzoooop! *crackle*
*
* <p>
* Animates a screen transition from on to off or off to on by applying
* some GL transformations to a screenshot.
*
* </p><p>
* This component must only be created or accessed by the {@link Looper} thread
* that belongs to the {@link DisplayPowerController}.
* </p>
*/
final class ElectronBeam {
private static final String TAG = "ElectronBeam";
@@ -65,7 +65,7 @@ final class ElectronBeam {
// Set to true when the animation context has been fully prepared.
private boolean mPrepared;
private boolean mWarmUp;
private int mMode;
private final Display mDisplay;
private final DisplayInfo mDisplayInfo = new DisplayInfo();
@@ -90,6 +90,10 @@ final class ElectronBeam {
private final FloatBuffer mVertexBuffer = createNativeFloatBuffer(8);
private final FloatBuffer mTexCoordBuffer = createNativeFloatBuffer(8);
public static final int MODE_WARM_UP = 0;
public static final int MODE_COOL_DOWN = 1;
public static final int MODE_BLANK = 2;
public ElectronBeam(Display display) {
mDisplay = display;
}
@@ -98,16 +102,15 @@ final class ElectronBeam {
* 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.
* @param mode The desired mode for the upcoming animation.
* @return True if the electron beam is ready, false if it is uncontrollable.
*/
public boolean prepare(boolean warmUp) {
public boolean prepare(int mode) {
if (DEBUG) {
Slog.d(TAG, "prepare: warmUp=" + warmUp);
Slog.d(TAG, "prepare: mode=" + mode);
}
mWarmUp = warmUp;
mMode = mode;
// Get the display size and adjust it for rotation.
mDisplay.getDisplayInfo(mDisplayInfo);
@@ -123,17 +126,28 @@ final class ElectronBeam {
}
// Prepare the surface for drawing.
if (!createEglContext()
|| !createEglSurface()
|| !captureScreenshotTextureAndSetViewport()) {
if (!tryPrepare()) {
dismiss();
return false;
}
// Done.
mPrepared = true;
return true;
}
private boolean tryPrepare() {
if (createSurface()) {
if (mMode == MODE_BLANK) {
return true;
}
return createEglContext()
&& createEglSurface()
&& captureScreenshotTextureAndSetViewport();
}
return false;
}
/**
* Dismisses the electron beam animation surface and cleans up.
*
@@ -148,6 +162,7 @@ final class ElectronBeam {
destroyScreenshotTexture();
destroyEglSurface();
destroySurface();
mPrepared = false;
}
@@ -163,6 +178,14 @@ final class ElectronBeam {
Slog.d(TAG, "drawFrame: level=" + level);
}
if (!mPrepared) {
return false;
}
if (mMode == MODE_BLANK) {
return showSurface(1.0f - level);
}
if (!attachEglContext()) {
return false;
}
@@ -185,8 +208,7 @@ final class ElectronBeam {
} finally {
detachEglContext();
}
return showEglSurface();
return showSurface(1.0f);
}
/**
@@ -217,7 +239,7 @@ final class ElectronBeam {
// 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);
mMode == MODE_WARM_UP ? 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,
@@ -251,7 +273,7 @@ final class ElectronBeam {
GLES10.glColorMask(true, true, true, true);
// draw the white highlight (we use the last vertices)
if (!mWarmUp) {
if (mMode == MODE_COOL_DOWN) {
GLES10.glColor4f(ag, ag, ag, 1.0f);
GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
}
@@ -472,7 +494,7 @@ final class ElectronBeam {
}
}*/
private boolean createEglSurface() {
private boolean createSurface() {
if (mSurfaceSession == null) {
mSurfaceSession = new SurfaceSession();
}
@@ -481,9 +503,15 @@ final class ElectronBeam {
try {
if (mSurface == null) {
try {
int flags;
if (mMode == MODE_BLANK) {
flags = Surface.FX_SURFACE_DIM | Surface.HIDDEN;
} else {
flags = Surface.OPAQUE | Surface.HIDDEN;
}
mSurface = new Surface(mSurfaceSession,
"ElectronBeam", mDisplayWidth, mDisplayHeight,
PixelFormat.OPAQUE, Surface.OPAQUE | Surface.HIDDEN);
PixelFormat.OPAQUE, flags);
} catch (Surface.OutOfResourcesException ex) {
Slog.e(TAG, "Unable to create surface.", ex);
return false;
@@ -514,7 +542,10 @@ final class ElectronBeam {
} finally {
Surface.closeTransaction();
}
return true;
}
private boolean createEglSurface() {
if (mEglSurface == null) {
int[] eglSurfaceAttribList = new int[] {
EGL14.EGL_NONE
@@ -536,7 +567,9 @@ final class ElectronBeam {
}
mEglSurface = null;
}
}
private void destroySurface() {
if (mSurface != null) {
Surface.openTransaction();
try {
@@ -549,11 +582,12 @@ final class ElectronBeam {
}
}
private boolean showEglSurface() {
private boolean showSurface(float alpha) {
if (!mSurfaceVisible) {
Surface.openTransaction();
try {
mSurface.setLayer(ELECTRON_BEAM_LAYER);
mSurface.setAlpha(alpha);
mSurface.show();
} finally {
Surface.closeTransaction();
@@ -643,7 +677,7 @@ final class ElectronBeam {
pw.println();
pw.println("Electron Beam State:");
pw.println(" mPrepared=" + mPrepared);
pw.println(" mWarmUp=" + mWarmUp);
pw.println(" mMode=" + mMode);
pw.println(" mDisplayLayerStack=" + mDisplayLayerStack);
pw.println(" mDisplayRotation=" + mDisplayRotation);
pw.println(" mDisplayWidth=" + mDisplayWidth);

View File

@@ -18,7 +18,7 @@ package com.android.server.power;
/**
* Low-level screen on blocker mechanism which is used to keep the screen off
* until the window manager is ready to show new content.
* or the contents of the screen hidden until the window manager is ready to show new content.
*/
interface ScreenOnBlocker {
/**