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. // When the screen turns on again, we report user activity to the power manager.
private boolean mScreenOffBecauseOfProximity; 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. // Set to true if the light sensor is enabled.
private boolean mLightSensorEnabled; private boolean mLightSensorEnabled;
@@ -513,7 +519,7 @@ final class DisplayPowerController {
final Executor executor = AsyncTask.THREAD_POOL_EXECUTOR; final Executor executor = AsyncTask.THREAD_POOL_EXECUTOR;
Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
mPowerState = new DisplayPowerState( mPowerState = new DisplayPowerState(
mElectronBeamAnimatesBacklightConfig ? null : new ElectronBeam(display), new ElectronBeam(display),
new PhotonicModulator(executor, new PhotonicModulator(executor,
mLights.getLight(LightsService.LIGHT_ID_BACKLIGHT), mLights.getLight(LightsService.LIGHT_ID_BACKLIGHT),
mSuspendBlocker)); mSuspendBlocker));
@@ -553,7 +559,6 @@ final class DisplayPowerController {
final boolean mustNotify; final boolean mustNotify;
boolean mustInitialize = false; boolean mustInitialize = false;
boolean updateAutoBrightness = mTwilightChanged; boolean updateAutoBrightness = mTwilightChanged;
boolean screenOnWasBlocked = false;
mTwilightChanged = false; mTwilightChanged = false;
synchronized (mLock) { synchronized (mLock) {
@@ -662,18 +667,24 @@ final class DisplayPowerController {
// It is relatively short but if we cancel it and switch to the // It is relatively short but if we cancel it and switch to the
// on animation immediately then the results are pretty ugly. // on animation immediately then the results are pretty ugly.
if (!mElectronBeamOffAnimator.isStarted()) { if (!mElectronBeamOffAnimator.isStarted()) {
if (mPowerRequest.blockScreenOn && !mPowerState.isScreenOn()) { // Turn the screen on. The contents of the screen may not yet
if (DEBUG) { // be visible if the electron beam has not been dismissed because
Slog.d(TAG, "Blocked screen on while screen currently off."); // its last frame of animation is solid black.
} setScreenOn(true);
screenOnWasBlocked = true;
if (mPowerRequest.blockScreenOn
&& mPowerState.getElectronBeamLevel() == 0.0f) {
blockScreenOn();
} else { } else {
setScreenOn(true); unblockScreenOn();
if (USE_ELECTRON_BEAM_ON_ANIMATION) { if (USE_ELECTRON_BEAM_ON_ANIMATION) {
if (!mElectronBeamOnAnimator.isStarted()) { if (!mElectronBeamOnAnimator.isStarted()) {
if (mPowerState.getElectronBeamLevel() == 1.0f) { if (mPowerState.getElectronBeamLevel() == 1.0f) {
mPowerState.dismissElectronBeam(); mPowerState.dismissElectronBeam();
} else if (mPowerState.prepareElectronBeam(true)) { } else if (mPowerState.prepareElectronBeam(
mElectronBeamAnimatesBacklightConfig ?
ElectronBeam.MODE_BLANK :
ElectronBeam.MODE_WARM_UP)) {
mElectronBeamOnAnimator.start(); mElectronBeamOnAnimator.start();
} else { } else {
mElectronBeamOnAnimator.end(); mElectronBeamOnAnimator.end();
@@ -684,22 +695,6 @@ final class DisplayPowerController {
mPowerState.dismissElectronBeam(); 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 { } else {
// Want screen off. // Want screen off.
@@ -708,7 +703,10 @@ final class DisplayPowerController {
if (!mElectronBeamOffAnimator.isStarted()) { if (!mElectronBeamOffAnimator.isStarted()) {
if (mPowerState.getElectronBeamLevel() == 0.0f) { if (mPowerState.getElectronBeamLevel() == 0.0f) {
setScreenOn(false); setScreenOn(false);
} else if (mPowerState.prepareElectronBeam(false) } else if (mPowerState.prepareElectronBeam(
mElectronBeamAnimatesBacklightConfig ?
ElectronBeam.MODE_BLANK :
ElectronBeam.MODE_COOL_DOWN)
&& mPowerState.isScreenOn()) { && mPowerState.isScreenOn()) {
mElectronBeamOffAnimator.start(); mElectronBeamOffAnimator.start();
} else { } else {
@@ -723,7 +721,7 @@ final class DisplayPowerController {
// We mostly care about the screen state here, ignoring brightness changes // We mostly care about the screen state here, ignoring brightness changes
// which will be handled asynchronously. // which will be handled asynchronously.
if (mustNotify if (mustNotify
&& !screenOnWasBlocked && !mScreenOnWasBlocked
&& !mElectronBeamOnAnimator.isStarted() && !mElectronBeamOnAnimator.isStarted()
&& !mElectronBeamOffAnimator.isStarted() && !mElectronBeamOffAnimator.isStarted()
&& mPowerState.waitUntilClean(mCleanListener)) { && 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) { private void setScreenOn(boolean on) {
if (!mPowerState.isScreenOn() == on) { if (!mPowerState.isScreenOn() == on) {
mPowerState.setScreenOn(on); mPowerState.setScreenOn(on);

View File

@@ -52,11 +52,14 @@ final class DisplayPowerRequest {
// If true, enables automatic brightness control. // If true, enables automatic brightness control.
public boolean useAutoBrightness; public boolean useAutoBrightness;
// If true, prevents the screen from turning on if it is currently off. // 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 the screen // The display does not enter a "ready" state if this flag is true and screen on is
// is off and is being prevented from turning on. The window manager policy blocks // blocked. The window manager policy blocks screen on while it prepares the keyguard to
// screen on while it prepares the keyguard to prevent the user from seeing // prevent the user from seeing intermediate updates.
// 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 boolean blockScreenOn;
public DisplayPowerRequest() { public DisplayPowerRequest() {

View File

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

View File

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