Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8b9cf1c8 authored by Jeff Brown's avatar Jeff Brown
Browse files

Reduce screen on latency, eliminate flashes.

Always use the ElectronBeam now, even when we are only animating
the backlight so that we will have a black surface remaining
on the screen after the screen turns off.

When turning on the screen, keep the black surface showing until
we unblock screen on then dismiss it as usual.

This change eliminates the flashing of old display content when
the screen is turned on.  It also helps to conceal some of the
latency of turning the screen on.  We always turn the screen on
immediately (even when screen on has nominally been blocked) and
rely on the black surface to hide the screen contents until the
last moment.  Dismissing the black surface is practically
instantaneous compared to turning the screen on.

Bug: 7299370
Bug: 7139924
Change-Id: I57d13287acd05bd0a48811095bb02dc7bc7cbeb6
parent 138f272b
Loading
Loading
Loading
Loading
+45 −27
Original line number Original line Diff line number Diff line
@@ -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.
                        }
                        screenOnWasBlocked = true;
                    } else {
                    setScreenOn(true);
                    setScreenOn(true);

                    if (mPowerRequest.blockScreenOn
                            && mPowerState.getElectronBeamLevel() == 0.0f) {
                        blockScreenOn();
                    } else {
                        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);
+8 −5
Original line number Original line Diff line number Diff line
@@ -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() {
+8 −17
Original line number Original line Diff line number Diff line
@@ -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,27 +130,20 @@ 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) {
            boolean success = mElectronBeam.prepare(warmUp);
        invalidate(DIRTY_ELECTRON_BEAM);
        invalidate(DIRTY_ELECTRON_BEAM);
            return success;
        return mElectronBeam.prepare(mode);
        } 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();
    }
    }
    }


    /**
    /**
     * Sets the level of the electron beam steering current.
     * Sets the level of the electron beam steering current.
@@ -230,10 +223,8 @@ 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) {
        if (mDirty == 0) {
        if (mDirty == 0) {
@@ -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);
            }
            }


+54 −20
Original line number Original line Diff line number Diff line
@@ -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);
+1 −1
Original line number Original line Diff line number Diff line
@@ -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 {
    /**
    /**