From fa42c51d0bec1caadd2ad8e48b57464365845ff8 Mon Sep 17 00:00:00 2001 From: Ahan Wu Date: Wed, 15 May 2019 19:52:51 +0800 Subject: [PATCH] Remove GLSurfaceView and release EGL context. We shouldn't use GLSurfaceView inside wallpaper so we just remove it. I addition, we also addressed a memory regression bug so we try release EGL context as possible to reduce memory usage. Bug: 131822567 Bug: 124127996 Test: Set a image wallpaper and observe. Test: adb shell dumpsys meminfo com.android.systemui Change-Id: I99bc2b45d28f93d473c845f235e22c8e846ea4cc --- .../com/android/systemui/ImageWallpaper.java | 238 ++++++++++++------ .../systemui/glwallpaper/EglHelper.java | 230 +++++++++++++++++ .../glwallpaper/GLWallpaperRenderer.java | 87 +++++++ .../glwallpaper/ImageGLWallpaper.java | 72 ------ .../glwallpaper/ImageRevealHelper.java | 25 +- .../glwallpaper/ImageWallpaperRenderer.java | 124 +++++---- 6 files changed, 551 insertions(+), 225 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java create mode 100644 packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java index ed2a6b59d1534..2be5743a84cd7 100644 --- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java @@ -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,112 +39,183 @@ 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 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 onAmbientModeChanged(boolean inAmbientMode, long animationDuration) { - if (mWallpaperSurfaceView != null) { - mWallpaperSurfaceView.notifyAmbientModeChanged(inAmbientMode, animationDuration); - } - } - - @Override - public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep, - float yOffsetStep, int xPixelOffset, int yPixelOffset) { - if (mWallpaperSurfaceView != null) { - mWallpaperSurfaceView.notifyOffsetsChanged(xOffset, yOffset); - } + 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(); } - private class GLWallpaperSurfaceView extends GLSurfaceView implements ImageGLView { - private WallpaperStatusListener mWallpaperStatusListener; + @Override + public void onSurfaceCreated(SurfaceHolder holder) { + mEglHelper.init(holder); + mRenderer.onSurfaceCreated(); + } - GLWallpaperSurfaceView(Context context) { - super(context); - setEGLContextClientVersion(2); - } + @Override + public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { + mRenderer.onSurfaceChanged(width, height); + mNeedRedraw = true; + } - @Override - public SurfaceHolder getHolder() { - return getSurfaceHolder(); - } - - @Override - public void setRenderer(Renderer renderer) { - super.setRenderer(renderer); - mWallpaperStatusListener = (WallpaperStatusListener) renderer; - } - - private void notifyAmbientModeChanged(boolean inAmbient, long duration) { - if (mWallpaperStatusListener != null) { - mWallpaperStatusListener.onAmbientModeChanged(inAmbient, duration); - } - } - - private void notifyOffsetsChanged(float xOffset, float yOffset) { - if (mWallpaperStatusListener != null) { - mWallpaperStatusListener.onOffsetsChanged( - xOffset, yOffset, getHolder().getSurfaceFrame()); - } - } - - @Override - public void render() { + @Override + public void onSurfaceRedrawNeeded(SurfaceHolder holder) { + if (mNeedRedraw) { + preRender(); requestRender(); + postRender(); + mNeedRedraw = false; } } - } - /** - * A listener to trace status of image wallpaper. - */ - public interface WallpaperStatusListener { + @Override + public SurfaceHolder getHolder() { + return getSurfaceHolder(); + } - /** - * 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 onStatePostChange() { + // When back to home, we try to release EGL, which is preserved in lock screen or aod. + if (mController.getState() == StatusBarState.SHADE) { + 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); - } + @Override + public void preRender() { + boolean contextRecreated = false; + Rect frame = getSurfaceHolder().getSurfaceFrame(); + getMainThreadHandler().removeCallbacks(mFinishRenderingTask); - /** - * An abstraction for view of GLRenderer. - */ - public interface ImageGLView { + // 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; + } + } - /** - * Ask the view to render. - */ - void render(); + // Check if we need to recreate egl surface. + if (mEglHelper.hasEglContext() && !mEglHelper.hasEglSurface()) { + if (!mEglHelper.createEglSurface(getSurfaceHolder())) { + Log.w(TAG, "recreate egl surface failed!"); + } + } + + // 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 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); + } + } + + @Override + public void postRender() { + scheduleFinishRendering(); + } + + private void scheduleFinishRendering() { + getMainThreadHandler().removeCallbacks(mFinishRenderingTask); + getMainThreadHandler().postDelayed(mFinishRenderingTask, DELAY_FINISH_RENDERING); + } + + private void finishRendering() { + if (mEglHelper != null) { + mEglHelper.destroyEglSurface(); + if (!needPreserveEglContext()) { + mEglHelper.destroyEglContext(); + } + } + } + + private boolean needPreserveEglContext() { + return mNeedTransition && mController != null + && mController.getState() == StatusBarState.KEYGUARD; + } } } diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java new file mode 100644 index 0000000000000..b66cc4f83412e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java @@ -0,0 +1,230 @@ +/* + * 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); + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java new file mode 100644 index 0000000000000..8cc17b6269466 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java @@ -0,0 +1,87 @@ +/* + * 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(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java index d935466757de1..4be7623f90f2a 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java @@ -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); - } } diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java index 5914236ab3493..6a1f24afe6200 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java @@ -56,11 +56,18 @@ class ImageRevealHelper { @Override public void onAnimationEnd(Animator animation) { - if (!mIsCanceled) { - mAwake = !mAwake; + if (!mIsCanceled && mRevealListener != null) { + mRevealListener.onRevealEnd(); } mIsCanceled = false; } + + @Override + public void onAnimationStart(Animator animation) { + if (mRevealListener != null) { + mRevealListener.onRevealStart(); + } + } }); } @@ -74,10 +81,6 @@ class ImageRevealHelper { return mReveal; } - public boolean isAwake() { - return mAwake; - } - void updateAwake(boolean awake, long duration) { mAwake = awake; mAnimator.setDuration(duration); @@ -93,5 +96,15 @@ class ImageRevealHelper { * Called back while reveal status changes. */ void onRevealStateChanged(); + + /** + * Called back while reveal starts. + */ + void onRevealStart(); + + /** + * Called back while reveal ends. + */ + void onRevealEnd(); } } diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java index 5bbfe84b63197..433e460ccfe8b 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java @@ -26,23 +26,17 @@ import android.app.WallpaperManager; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Rect; -import android.opengl.GLSurfaceView; -import android.os.Build; import android.util.Log; import android.util.MathUtils; +import android.util.Size; -import com.android.systemui.ImageWallpaper; -import com.android.systemui.ImageWallpaper.ImageGLView; import com.android.systemui.R; -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.opengles.GL10; - /** * A GL renderer for image wallpaper. */ -public class ImageWallpaperRenderer implements GLSurfaceView.Renderer, - ImageWallpaper.WallpaperStatusListener, ImageRevealHelper.RevealStateListener { +public class ImageWallpaperRenderer implements GLWallpaperRenderer, + ImageRevealHelper.RevealStateListener { private static final String TAG = ImageWallpaperRenderer.class.getSimpleName(); private static final float SCALE_VIEWPORT_MIN = 0.98f; private static final float SCALE_VIEWPORT_MAX = 1f; @@ -52,62 +46,61 @@ public class ImageWallpaperRenderer implements GLSurfaceView.Renderer, private final ImageGLWallpaper mWallpaper; private final ImageProcessHelper mImageProcessHelper; private final ImageRevealHelper mImageRevealHelper; - private final ImageGLView mGLView; - private float mXOffset = 0f; - private float mYOffset = 0f; - private int mWidth = 0; - private int mHeight = 0; + private SurfaceProxy mProxy; + private Rect mSurfaceSize; private Bitmap mBitmap; - private int mBitmapWidth = 0; - private int mBitmapHeight = 0; - public ImageWallpaperRenderer(Context context, ImageGLView glView) { + public ImageWallpaperRenderer(Context context, SurfaceProxy proxy) { mWallpaperManager = context.getSystemService(WallpaperManager.class); if (mWallpaperManager == null) { Log.w(TAG, "WallpaperManager not available"); } + mProxy = proxy; mProgram = new ImageGLProgram(context); mWallpaper = new ImageGLWallpaper(mProgram); mImageProcessHelper = new ImageProcessHelper(); mImageRevealHelper = new ImageRevealHelper(this); - mGLView = glView; - if (mWallpaperManager != null) { - mBitmap = mWallpaperManager.getBitmap(); - mBitmapWidth = mBitmap.getWidth(); - mBitmapHeight = mBitmap.getHeight(); + if (loadBitmap()) { // Compute threshold of the image, this is an async work. mImageProcessHelper.start(mBitmap); - mWallpaperManager.forgetLoadedWallpaper(); } } @Override - public void onSurfaceCreated(GL10 gl, EGLConfig config) { + public void onSurfaceCreated() { glClearColor(0f, 0f, 0f, 1.0f); mProgram.useGLProgram( R.raw.image_wallpaper_vertex_shader, R.raw.image_wallpaper_fragment_shader); + + if (!loadBitmap()) { + Log.w(TAG, "reload bitmap failed!"); + } + mWallpaper.setup(mBitmap); mBitmap = null; } - @Override - public void onSurfaceChanged(GL10 gl, int width, int height) { - glViewport(0, 0, width, height); - if (Build.IS_DEBUGGABLE) { - Log.d(TAG, "onSurfaceChanged: width=" + width + ", height=" + height - + ", xOffset=" + mXOffset + ", yOffset=" + mYOffset); + private boolean loadBitmap() { + if (mWallpaperManager != null && mBitmap == null) { + mBitmap = mWallpaperManager.getBitmap(); + mWallpaperManager.forgetLoadedWallpaper(); + if (mBitmap != null) { + mSurfaceSize = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); + } } - mWidth = width; - mHeight = height; - mWallpaper.adjustTextureCoordinates( - mBitmapWidth, mBitmapHeight, width, height, mXOffset, mYOffset); + return mBitmap != null; } @Override - public void onDrawFrame(GL10 gl) { + public void onSurfaceChanged(int width, int height) { + glViewport(0, 0, width, height); + } + + @Override + public void onDrawFrame() { float threshold = mImageProcessHelper.getThreshold(); float reveal = mImageRevealHelper.getReveal(); @@ -122,50 +115,45 @@ public class ImageWallpaperRenderer implements GLSurfaceView.Renderer, mWallpaper.draw(); } + @Override + public void updateAmbientMode(boolean inAmbientMode, long duration) { + mImageRevealHelper.updateAwake(!inAmbientMode, duration); + } + + @Override + public Size reportSurfaceSize() { + return new Size(mSurfaceSize.width(), mSurfaceSize.height()); + } + + @Override + public void finish() { + mProxy = null; + } + private void scaleViewport(float reveal) { + int width = mSurfaceSize.width(); + int height = mSurfaceSize.height(); // Interpolation between SCALE_VIEWPORT_MAX and SCALE_VIEWPORT_MIN by reveal. float vpScaled = MathUtils.lerp(SCALE_VIEWPORT_MAX, SCALE_VIEWPORT_MIN, reveal); // Calculate the offset amount from the lower left corner. float offset = (SCALE_VIEWPORT_MAX - vpScaled) / 2; // Change the viewport. - glViewport((int) (mWidth * offset), (int) (mHeight * offset), - (int) (mWidth * vpScaled), (int) (mHeight * vpScaled)); - } - - @Override - public void onAmbientModeChanged(boolean inAmbientMode, long duration) { - mImageRevealHelper.updateAwake(!inAmbientMode, duration); - requestRender(); - } - - @Override - public void onOffsetsChanged(float xOffset, float yOffset, Rect frame) { - if (frame == null || (xOffset == mXOffset && yOffset == mYOffset)) { - return; - } - - int width = frame.width(); - int height = frame.height(); - mXOffset = xOffset; - mYOffset = yOffset; - - if (Build.IS_DEBUGGABLE) { - Log.d(TAG, "onOffsetsChanged: width=" + width + ", height=" + height - + ", xOffset=" + mXOffset + ", yOffset=" + mYOffset); - } - mWallpaper.adjustTextureCoordinates( - mBitmapWidth, mBitmapHeight, width, height, mXOffset, mYOffset); - requestRender(); + glViewport((int) (width * offset), (int) (height * offset), + (int) (width * vpScaled), (int) (height * vpScaled)); } @Override public void onRevealStateChanged() { - requestRender(); + mProxy.requestRender(); } - private void requestRender() { - if (mGLView != null) { - mGLView.render(); - } + @Override + public void onRevealStart() { + mProxy.preRender(); + } + + @Override + public void onRevealEnd() { + mProxy.postRender(); } }