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

Commit 10472627 authored by Android Build Merger (Role)'s avatar Android Build Merger (Role) Committed by Android (Google) Code Review
Browse files

Merge "Merge "Remove GLSurfaceView and release EGL context." into qt-dev am:...

Merge "Merge "Remove GLSurfaceView and release EGL context." into qt-dev am: 2d6f9aa6 am: df132d6a" into qt-r1-dev-plus-aosp
parents ea1e67c4 84ac7464
Loading
Loading
Loading
Loading
+150 −70
Original line number Diff line number Diff line
@@ -16,13 +16,22 @@

package com.android.systemui;

import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Rect;
import android.opengl.GLSurfaceView;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
import android.util.Size;
import android.view.SurfaceHolder;

import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.glwallpaper.EglHelper;
import com.android.systemui.glwallpaper.GLWallpaperRenderer;
import com.android.systemui.glwallpaper.ImageWallpaperRenderer;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.DozeParameters;

/**
 * Default built-in wallpaper that simply shows a static image.
@@ -30,51 +39,98 @@ import com.android.systemui.glwallpaper.ImageWallpaperRenderer;
@SuppressWarnings({"UnusedDeclaration"})
public class ImageWallpaper extends WallpaperService {
    private static final String TAG = ImageWallpaper.class.getSimpleName();
    // We delayed destroy render context that subsequent render requests have chance to cancel it.
    // This is to avoid destroying then recreating render context in a very short time.
    private static final int DELAY_FINISH_RENDERING = 1000;

    @Override
    public Engine onCreateEngine() {
        return new GLEngine(this);
    }

    class GLEngine extends Engine {
        private GLWallpaperSurfaceView mWallpaperSurfaceView;
    class GLEngine extends Engine implements GLWallpaperRenderer.SurfaceProxy, StateListener {
        // Surface is rejected if size below a threshold on some devices (ie. 8px on elfin)
        // set min to 64 px (CTS covers this), please refer to ag/4867989 for detail.
        @VisibleForTesting
        static final int MIN_SURFACE_WIDTH = 64;
        @VisibleForTesting
        static final int MIN_SURFACE_HEIGHT = 64;

        private GLWallpaperRenderer mRenderer;
        private EglHelper mEglHelper;
        private StatusBarStateController mController;
        private final Runnable mFinishRenderingTask = this::finishRendering;
        private final boolean mNeedTransition;
        private boolean mNeedRedraw;

        GLEngine(Context context) {
            mWallpaperSurfaceView = new GLWallpaperSurfaceView(context);
            mWallpaperSurfaceView.setRenderer(
                    new ImageWallpaperRenderer(context, mWallpaperSurfaceView));
            mWallpaperSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
            setOffsetNotificationsEnabled(true);
            mNeedTransition = ActivityManager.isHighEndGfx()
                    && !DozeParameters.getInstance(context).getDisplayNeedsBlanking();

            // We will preserve EGL context when we are in lock screen or aod
            // to avoid janking in following transition, we need to release when back to home.
            mController = Dependency.get(StatusBarStateController.class);
            if (mController != null) {
                mController.addCallback(this /* StateListener */);
            }
            mEglHelper = new EglHelper();
            mRenderer = new ImageWallpaperRenderer(context, this /* SurfaceProxy */);
        }

        @Override
        public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
            if (mWallpaperSurfaceView != null) {
                mWallpaperSurfaceView.notifyAmbientModeChanged(inAmbientMode, animationDuration);
        public void onCreate(SurfaceHolder surfaceHolder) {
            setFixedSizeAllowed(true);
            setOffsetNotificationsEnabled(false);
            updateSurfaceSize();
        }

        private void updateSurfaceSize() {
            SurfaceHolder holder = getSurfaceHolder();
            Size frameSize = mRenderer.reportSurfaceSize();
            int width = Math.max(MIN_SURFACE_WIDTH, frameSize.getWidth());
            int height = Math.max(MIN_SURFACE_HEIGHT, frameSize.getHeight());
            holder.setFixedSize(width, height);
        }

        @Override
        public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep,
                float yOffsetStep, int xPixelOffset, int yPixelOffset) {
            if (mWallpaperSurfaceView != null) {
                mWallpaperSurfaceView.notifyOffsetsChanged(xOffset, yOffset);
            }
        public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
            mRenderer.updateAmbientMode(inAmbientMode,
                    (mNeedTransition || animationDuration != 0) ? animationDuration : 0);
        }

        @Override
        public void onDestroy() {
            if (mWallpaperSurfaceView != null) {
                mWallpaperSurfaceView.onPause();
            if (mController != null) {
                mController.removeCallback(this /* StateListener */);
            }
            mController = null;
            mRenderer.finish();
            mRenderer = null;
            mEglHelper.finish();
            mEglHelper = null;
            getSurfaceHolder().getSurface().hwuiDestroy();
        }

        @Override
        public void onSurfaceCreated(SurfaceHolder holder) {
            mEglHelper.init(holder);
            mRenderer.onSurfaceCreated();
        }

        private class GLWallpaperSurfaceView extends GLSurfaceView implements ImageGLView {
            private WallpaperStatusListener mWallpaperStatusListener;
        @Override
        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            mRenderer.onSurfaceChanged(width, height);
            mNeedRedraw = true;
        }

            GLWallpaperSurfaceView(Context context) {
                super(context);
                setEGLContextClientVersion(2);
        @Override
        public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
            if (mNeedRedraw) {
                preRender();
                requestRender();
                postRender();
                mNeedRedraw = false;
            }
        }

        @Override
@@ -83,59 +139,83 @@ public class ImageWallpaper extends WallpaperService {
        }

        @Override
            public void setRenderer(Renderer renderer) {
                super.setRenderer(renderer);
                mWallpaperStatusListener = (WallpaperStatusListener) renderer;
        public void onStatePostChange() {
            // When back to home, we try to release EGL, which is preserved in lock screen or aod.
            if (mController.getState() == StatusBarState.SHADE) {
                scheduleFinishRendering();
            }
        }

        @Override
        public void preRender() {
            boolean contextRecreated = false;
            Rect frame = getSurfaceHolder().getSurfaceFrame();
            getMainThreadHandler().removeCallbacks(mFinishRenderingTask);

            // Check if we need to recreate egl context.
            if (!mEglHelper.hasEglContext()) {
                mEglHelper.destroyEglSurface();
                if (!mEglHelper.createEglContext()) {
                    Log.w(TAG, "recreate egl context failed!");
                } else {
                    contextRecreated = true;
                }
            }

            private void notifyAmbientModeChanged(boolean inAmbient, long duration) {
                if (mWallpaperStatusListener != null) {
                    mWallpaperStatusListener.onAmbientModeChanged(inAmbient, duration);
            // Check if we need to recreate egl surface.
            if (mEglHelper.hasEglContext() && !mEglHelper.hasEglSurface()) {
                if (!mEglHelper.createEglSurface(getSurfaceHolder())) {
                    Log.w(TAG, "recreate egl surface failed!");
                }
            }

            private void notifyOffsetsChanged(float xOffset, float yOffset) {
                if (mWallpaperStatusListener != null) {
                    mWallpaperStatusListener.onOffsetsChanged(
                            xOffset, yOffset, getHolder().getSurfaceFrame());
            // If we recreate egl context, notify renderer to setup again.
            if (mEglHelper.hasEglContext() && mEglHelper.hasEglSurface() && contextRecreated) {
                mRenderer.onSurfaceCreated();
                mRenderer.onSurfaceChanged(frame.width(), frame.height());
            }
        }

        @Override
            public void render() {
                requestRender();
        public void requestRender() {
            Rect frame = getSurfaceHolder().getSurfaceFrame();
            boolean readyToRender = mEglHelper.hasEglContext() && mEglHelper.hasEglSurface()
                    && frame.width() > 0 && frame.height() > 0;

            if (readyToRender) {
                mRenderer.onDrawFrame();
                if (!mEglHelper.swapBuffer()) {
                    Log.e(TAG, "drawFrame failed!");
                }
            } else {
                Log.e(TAG, "requestRender: not ready, has context=" + mEglHelper.hasEglContext()
                        + ", has surface=" + mEglHelper.hasEglSurface()
                        + ", frame=" + frame);
            }
        }

    /**
     * A listener to trace status of image wallpaper.
     */
    public interface WallpaperStatusListener {

        /**
         * Called back while ambient mode changes.
         * @param inAmbientMode true if is in ambient mode, false otherwise.
         * @param duration the duration of animation.
         */
        void onAmbientModeChanged(boolean inAmbientMode, long duration);
        @Override
        public void postRender() {
            scheduleFinishRendering();
        }

        /**
         * Called back while wallpaper offsets.
         * @param xOffset The offset portion along x.
         * @param yOffset The offset portion along y.
         */
        void onOffsetsChanged(float xOffset, float yOffset, Rect frame);
        private void scheduleFinishRendering() {
            getMainThreadHandler().removeCallbacks(mFinishRenderingTask);
            getMainThreadHandler().postDelayed(mFinishRenderingTask, DELAY_FINISH_RENDERING);
        }

    /**
     * An abstraction for view of GLRenderer.
     */
    public interface ImageGLView {
        private void finishRendering() {
            if (mEglHelper != null) {
                mEglHelper.destroyEglSurface();
                if (!needPreserveEglContext()) {
                    mEglHelper.destroyEglContext();
                }
            }
        }

        /**
         * Ask the view to render.
         */
        void render();
        private boolean needPreserveEglContext() {
            return mNeedTransition && mController != null
                    && mController.getState() == StatusBarState.KEYGUARD;
        }
    }
}
+230 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.systemui.glwallpaper;

import static android.opengl.EGL14.EGL_ALPHA_SIZE;
import static android.opengl.EGL14.EGL_BLUE_SIZE;
import static android.opengl.EGL14.EGL_CONFIG_CAVEAT;
import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION;
import static android.opengl.EGL14.EGL_DEFAULT_DISPLAY;
import static android.opengl.EGL14.EGL_DEPTH_SIZE;
import static android.opengl.EGL14.EGL_GREEN_SIZE;
import static android.opengl.EGL14.EGL_NONE;
import static android.opengl.EGL14.EGL_NO_CONTEXT;
import static android.opengl.EGL14.EGL_NO_DISPLAY;
import static android.opengl.EGL14.EGL_NO_SURFACE;
import static android.opengl.EGL14.EGL_OPENGL_ES2_BIT;
import static android.opengl.EGL14.EGL_RED_SIZE;
import static android.opengl.EGL14.EGL_RENDERABLE_TYPE;
import static android.opengl.EGL14.EGL_STENCIL_SIZE;
import static android.opengl.EGL14.EGL_SUCCESS;
import static android.opengl.EGL14.eglChooseConfig;
import static android.opengl.EGL14.eglCreateContext;
import static android.opengl.EGL14.eglCreateWindowSurface;
import static android.opengl.EGL14.eglDestroyContext;
import static android.opengl.EGL14.eglDestroySurface;
import static android.opengl.EGL14.eglGetDisplay;
import static android.opengl.EGL14.eglGetError;
import static android.opengl.EGL14.eglInitialize;
import static android.opengl.EGL14.eglMakeCurrent;
import static android.opengl.EGL14.eglSwapBuffers;
import static android.opengl.EGL14.eglTerminate;

import android.opengl.EGLConfig;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
import android.opengl.GLUtils;
import android.util.Log;
import android.view.SurfaceHolder;

/**
 * A helper class to handle EGL management.
 */
public class EglHelper {
    private static final String TAG = EglHelper.class.getSimpleName();

    private EGLDisplay mEglDisplay;
    private EGLConfig mEglConfig;
    private EGLContext mEglContext;
    private EGLSurface mEglSurface;

    /**
     * Initialize EGL and prepare EglSurface.
     * @param surfaceHolder surface holder.
     * @return true if EglSurface is ready.
     */
    public boolean init(SurfaceHolder surfaceHolder) {
        mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
        if (mEglDisplay == EGL_NO_DISPLAY) {
            Log.w(TAG, "eglGetDisplay failed: " + GLUtils.getEGLErrorString(eglGetError()));
            return false;
        }

        if (!eglInitialize(mEglDisplay, null, 0, null, 0)) {
            Log.w(TAG, "eglInitialize failed: " + GLUtils.getEGLErrorString(eglGetError()));
            return false;
        }

        mEglConfig = chooseEglConfig();
        if (mEglConfig == null) {
            Log.w(TAG, "eglConfig not initialized!");
            return false;
        }

        if (!createEglContext()) {
            Log.w(TAG, "Can't create EGLContext!");
            return false;
        }

        if (!createEglSurface(surfaceHolder)) {
            Log.w(TAG, "Can't create EGLSurface!");
            return false;
        }

        return true;
    }

    private EGLConfig chooseEglConfig() {
        int[] configsCount = new int[1];
        EGLConfig[] configs = new EGLConfig[1];
        int[] configSpec = getConfig();
        if (!eglChooseConfig(mEglDisplay, configSpec, 0, configs, 0, 1, configsCount, 0)) {
            Log.w(TAG, "eglChooseConfig failed: " + GLUtils.getEGLErrorString(eglGetError()));
            return null;
        } else {
            if (configsCount[0] <= 0) {
                Log.w(TAG, "eglChooseConfig failed, invalid configs count: " + configsCount[0]);
                return null;
            } else {
                return configs[0];
            }
        }
    }

    private int[] getConfig() {
        return new int[] {
            EGL_RED_SIZE, 8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE, 8,
            EGL_ALPHA_SIZE, 8,
            EGL_DEPTH_SIZE, 0,
            EGL_STENCIL_SIZE, 0,
            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
            EGL_CONFIG_CAVEAT, EGL_NONE,
            EGL_NONE
        };
    }

    /**
     * Prepare an EglSurface.
     * @param surfaceHolder surface holder.
     * @return true if EglSurface is ready.
     */
    public boolean createEglSurface(SurfaceHolder surfaceHolder) {
        mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null, 0);
        if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
            Log.w(TAG, "createWindowSurface failed: " + GLUtils.getEGLErrorString(eglGetError()));
            return false;
        }

        if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
            Log.w(TAG, "eglMakeCurrent failed: " + GLUtils.getEGLErrorString(eglGetError()));
            return false;
        }

        return true;
    }

    /**
     * Destroy EglSurface.
     */
    public void destroyEglSurface() {
        if (hasEglSurface()) {
            eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
            eglDestroySurface(mEglDisplay, mEglSurface);
            mEglSurface = null;
        }
    }

    /**
     * Check if we have a valid EglSurface.
     * @return true if EglSurface is ready.
     */
    public boolean hasEglSurface() {
        return mEglSurface != null && mEglSurface != EGL_NO_SURFACE;
    }

    /**
     * Prepare EglContext.
     * @return true if EglContext is ready.
     */
    public boolean createEglContext() {
        int[] attrib_list = new int[] {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
        mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attrib_list, 0);
        if (mEglContext == EGL_NO_CONTEXT) {
            Log.w(TAG, "eglCreateContext failed: " + GLUtils.getEGLErrorString(eglGetError()));
            return false;
        }
        return true;
    }

    /**
     * Destroy EglContext.
     */
    public void destroyEglContext() {
        if (hasEglContext()) {
            eglDestroyContext(mEglDisplay, mEglContext);
            mEglContext = null;
        }
    }

    /**
     * Check if we have EglContext.
     * @return true if EglContext is ready.
     */
    public boolean hasEglContext() {
        return mEglContext != null;
    }

    /**
     * Swap buffer to display.
     * @return true if swap successfully.
     */
    public boolean swapBuffer() {
        boolean status = eglSwapBuffers(mEglDisplay, mEglSurface);
        int error = eglGetError();
        if (error != EGL_SUCCESS) {
            Log.w(TAG, "eglSwapBuffers failed: " + GLUtils.getEGLErrorString(error));
        }
        return status;
    }

    /**
     * Destroy EglSurface and EglContext, then terminate EGL.
     */
    public void finish() {
        if (hasEglSurface()) {
            destroyEglSurface();
        }
        if (hasEglContext()) {
            destroyEglContext();
        }
        eglTerminate(mEglDisplay);
    }

}
+87 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.systemui.glwallpaper;

import android.util.Size;
import android.view.SurfaceHolder;

/**
 * A renderer which is responsible for making OpenGL calls to render a frame.
 */
public interface GLWallpaperRenderer {

    /**
     * Called when the surface is created or recreated.
     */
    void onSurfaceCreated();

    /**
     * Called when the surface changed size.
     * @param width surface width.
     * @param height surface height.
     */
    void onSurfaceChanged(int width, int height);

    /**
     * Called to draw the current frame.
     */
    void onDrawFrame();

    /**
     * Notify ambient mode is changed.
     * @param inAmbientMode true if in ambient mode.
     * @param duration duration of transition.
     */
    void updateAmbientMode(boolean inAmbientMode, long duration);

    /**
     * Ask renderer to report the surface size it needs.
     */
    Size reportSurfaceSize();

    /**
     * Called when no need to render any more.
     */
    void finish();

    /**
     * A proxy which owns surface holder.
     */
    interface SurfaceProxy {

        /**
         * Get surface holder.
         * @return surface holder.
         */
        SurfaceHolder getHolder();

        /**
         * Ask proxy to start rendering frame to surface.
         */
        void requestRender();

        /**
         * Ask proxy to prepare render context.
         */
        void preRender();

        /**
         * Ask proxy to destroy render context.
         */
        void postRender();
    }
}
+0 −72
Original line number Diff line number Diff line
@@ -195,76 +195,4 @@ class ImageGLWallpaper {
        glUniform1i(mUniTexture, 0);
    }

    /**
     * This method adjust s(x-axis), t(y-axis) texture coordinates
     * to prevent the wallpaper from being stretched.
     * The adjustment happens if either the width or height of the bitmap is larger than
     * corresponding size of the surface.
     * If both width and height are larger than corresponding size of the surface,
     * the adjustment will happen at both s, t side.
     *
     * @param bitmapWidth The width of the bitmap.
     * @param bitmapHeight The height of the bitmap.
     * @param surfaceWidth The width of the surface.
     * @param surfaceHeight The height of the surface.
     * @param xOffset The offset amount along s axis.
     * @param yOffset The offset amount along t axis.
     */
    void adjustTextureCoordinates(int bitmapWidth, int bitmapHeight,
            int surfaceWidth, int surfaceHeight, float xOffset, float yOffset) {
        float[] coordinates = TEXTURES.clone();

        if (bitmapWidth > surfaceWidth) {
            // Calculate the new s pos in pixels.
            float pixelS = (float) Math.round((bitmapWidth - surfaceWidth) * xOffset);
            // Calculate the s pos in texture coordinate.
            float coordinateS = pixelS / bitmapWidth;
            // Calculate the percentage occupied by the surface width in bitmap width.
            float surfacePercentageW = (float) surfaceWidth / bitmapWidth;
            // Need also consider the case if bitmap height is smaller than surface height.
            if (bitmapHeight < surfaceHeight) {
                // We will narrow the surface percentage to keep aspect ratio.
                surfacePercentageW *= (float) bitmapHeight / surfaceHeight;
            }
            // Determine the final s pos, also limit the legal s pos to prevent from out of range.
            float s = coordinateS + surfacePercentageW > 1f ? 1f - surfacePercentageW : coordinateS;
            // Traverse the s pos in texture coordinates array and adjust the s pos accordingly.
            for (int i = 0; i < coordinates.length; i += 2) {
                // indices 2, 4 and 6 are the end of s coordinates.
                if (i == 2 || i == 4 || i == 6) {
                    coordinates[i] = Math.min(1f, s + surfacePercentageW);
                } else {
                    coordinates[i] = s;
                }
            }
        }

        if (bitmapHeight > surfaceHeight) {
            // Calculate the new t pos in pixels.
            float pixelT = (float) Math.round((bitmapHeight - surfaceHeight) * yOffset);
            // Calculate the t pos in texture coordinate.
            float coordinateT = pixelT / bitmapHeight;
            // Calculate the percentage occupied by the surface height in bitmap height.
            float surfacePercentageH = (float) surfaceHeight / bitmapHeight;
            // Need also consider the case if bitmap width is smaller than surface width.
            if (bitmapWidth < surfaceWidth) {
                // We will narrow the surface percentage to keep aspect ratio.
                surfacePercentageH *= (float) bitmapWidth / surfaceWidth;
            }
            // Determine the final t pos, also limit the legal t pos to prevent from out of range.
            float t = coordinateT + surfacePercentageH > 1f ? 1f - surfacePercentageH : coordinateT;
            // Traverse the t pos in texture coordinates array and adjust the t pos accordingly.
            for (int i = 1; i < coordinates.length; i += 2) {
                // indices 1, 3 and 11 are the end of t coordinates.
                if (i == 1 || i == 3 || i == 11) {
                    coordinates[i] = Math.min(1f, t + surfacePercentageH);
                } else {
                    coordinates[i] = t;
                }
            }
        }

        mTextureBuffer.put(coordinates);
        mTextureBuffer.position(0);
    }
}
+19 −6

File changed.

Preview size limit exceeded, changes collapsed.

Loading