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

Commit c741fbb9 authored by Aurélien Pomini's avatar Aurélien Pomini Committed by Automerger Merge Worker
Browse files

Merge "Remove usage of GlEngine" into tm-qpr-dev am: 5538ee1c

parents 70aeb5a6 5538ee1c
Loading
Loading
Loading
Loading
+0 −5
Original line number Diff line number Diff line
@@ -23,11 +23,6 @@
        xmlns:tools="http://schemas.android.com/tools"
        coreApp="true">

    <!-- Using OpenGL ES 2.0 -->
    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true" />

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

    <!-- Used to read wallpaper -->
+0 −11
Original line number Diff line number Diff line
precision mediump float;

// The actual wallpaper texture.
uniform sampler2D uTexture;

varying vec2 vTextureCoordinates;

void main() {
    // gets the pixel value of the wallpaper for this uv coordinates on screen.
    gl_FragColor = texture2D(uTexture, vTextureCoordinates);
}
 No newline at end of file
+0 −8
Original line number Diff line number Diff line
attribute vec4 aPosition;
attribute vec2 aTextureCoordinates;
varying vec2 vTextureCoordinates;

void main() {
    vTextureCoordinates = aTextureCoordinates;
    gl_Position = aPosition;
}
 No newline at end of file
+0 −4
Original line number Diff line number Diff line
@@ -254,10 +254,6 @@ object Flags {
    // TODO(b/254512848): Tracking Bug
    val REGION_SAMPLING = unreleasedFlag(801, "region_sampling", teamfood = true)

    // 802 - wallpaper rendering
    // TODO(b/254512923): Tracking Bug
    @JvmField val USE_CANVAS_RENDERER = unreleasedFlag(802, "use_canvas_renderer")

    // 803 - screen contents translation
    // TODO(b/254513187): Tracking Bug
    val SCREEN_CONTENTS_TRANSLATION = unreleasedFlag(803, "screen_contents_translation")
+9 −452
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package com.android.systemui.wallpapers;

import static com.android.systemui.flags.Flags.USE_CANVAS_RENDERER;

import android.app.WallpaperColors;
import android.app.WallpaperManager;
import android.graphics.Bitmap;
@@ -27,16 +25,10 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.service.wallpaper.WallpaperService;
import android.util.ArraySet;
import android.util.Log;
import android.util.MathUtils;
import android.util.Size;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.WindowManager;
@@ -45,16 +37,10 @@ import androidx.annotation.NonNull;

import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.wallpapers.canvas.WallpaperLocalColorExtractor;
import com.android.systemui.wallpapers.gl.EglHelper;
import com.android.systemui.wallpapers.gl.ImageWallpaperRenderer;

import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import javax.inject.Inject;
@@ -64,455 +50,32 @@ import javax.inject.Inject;
 */
@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;
    private static final @android.annotation.NonNull RectF LOCAL_COLOR_BOUNDS =
            new RectF(0, 0, 1, 1);
    private static final boolean DEBUG = false;

    private final ArrayList<RectF> mLocalColorsToAdd = new ArrayList<>();
    private final ArraySet<RectF> mColorAreas = new ArraySet<>();
    // keep track of the number of pages of the launcher for local color extraction purposes
    private volatile int mPages = 1;
    private boolean mPagesComputed = false;
    private HandlerThread mWorker;
    // scaled down version
    private Bitmap mMiniBitmap;
    private final FeatureFlags mFeatureFlags;

    // used in canvasEngine to load/unload the bitmap and extract the colors
    // used for most tasks (call canvas.drawBitmap, load/unload the bitmap)
    @Background
    private final DelayableExecutor mBackgroundExecutor;

    // wait at least this duration before unloading the bitmap
    private static final int DELAY_UNLOAD_BITMAP = 2000;

    @Inject
    public ImageWallpaper(FeatureFlags featureFlags,
            @Background DelayableExecutor backgroundExecutor) {
    public ImageWallpaper(@Background DelayableExecutor backgroundExecutor) {
        super();
        mFeatureFlags = featureFlags;
        mBackgroundExecutor = backgroundExecutor;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mWorker = new HandlerThread(TAG);
        mWorker.start();
    }

    @Override
    public Engine onCreateEngine() {
        return mFeatureFlags.isEnabled(USE_CANVAS_RENDERER) ? new CanvasEngine() : new GLEngine();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mWorker.quitSafely();
        mWorker = null;
        mMiniBitmap = null;
    }

    class GLEngine extends Engine implements DisplayListener {
        // 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 = 128;
        @VisibleForTesting
        static final int MIN_SURFACE_HEIGHT = 128;

        private ImageWallpaperRenderer mRenderer;
        private EglHelper mEglHelper;
        private final Runnable mFinishRenderingTask = this::finishRendering;
        private boolean mNeedRedraw;

        private boolean mDisplaySizeValid = false;
        private int mDisplayWidth = 1;
        private int mDisplayHeight = 1;

        private int mImgWidth = 1;
        private int mImgHeight = 1;

        GLEngine() { }

        @VisibleForTesting
        GLEngine(Handler handler) {
            super(SystemClock::elapsedRealtime, handler);
        }

        @Override
        public void onCreate(SurfaceHolder surfaceHolder) {
            Trace.beginSection("ImageWallpaper.Engine#onCreate");
            mEglHelper = getEglHelperInstance();
            // Deferred init renderer because we need to get wallpaper by display context.
            mRenderer = getRendererInstance();
            setFixedSizeAllowed(true);
            updateSurfaceSize();
            setShowForAllUsers(true);

            mRenderer.setOnBitmapChanged(b -> {
                mLocalColorsToAdd.addAll(mColorAreas);
                if (mLocalColorsToAdd.size() > 0) {
                    updateMiniBitmapAndNotify(b);
                }
            });
            getDisplayContext().getSystemService(DisplayManager.class)
                    .registerDisplayListener(this, mWorker.getThreadHandler());
            Trace.endSection();
        }

        @Override
        public void onDisplayAdded(int displayId) { }

        @Override
        public void onDisplayRemoved(int displayId) { }

        @Override
        public void onDisplayChanged(int displayId) {
            if (displayId == getDisplayContext().getDisplayId()) {
                mDisplaySizeValid = false;
            }
        }

        EglHelper getEglHelperInstance() {
            return new EglHelper();
        }

        ImageWallpaperRenderer getRendererInstance() {
            return new ImageWallpaperRenderer(getDisplayContext());
        }

        @Override
        public void onOffsetsChanged(float xOffset, float yOffset,
                float xOffsetStep, float yOffsetStep,
                int xPixelOffset, int yPixelOffset) {
            final int pages;
            if (xOffsetStep > 0 && xOffsetStep <= 1) {
                pages = (int) Math.round(1 / xOffsetStep) + 1;
            } else {
                pages = 1;
            }
            if (pages == mPages) return;
            mPages = pages;
            if (mMiniBitmap == null || mMiniBitmap.isRecycled()) return;
            mWorker.getThreadHandler().post(() ->
                    computeAndNotifyLocalColors(new ArrayList<>(mColorAreas), mMiniBitmap));
        }

        private void updateMiniBitmapAndNotify(Bitmap b) {
            if (b == null) return;
            int size = Math.min(b.getWidth(), b.getHeight());
            float scale = 1.0f;
            if (size > MIN_SURFACE_WIDTH) {
                scale = (float) MIN_SURFACE_WIDTH / (float) size;
            }
            mImgHeight = b.getHeight();
            mImgWidth = b.getWidth();
            mMiniBitmap = Bitmap.createScaledBitmap(b,  (int) Math.max(scale * b.getWidth(), 1),
                    (int) Math.max(scale * b.getHeight(), 1), false);
            computeAndNotifyLocalColors(mLocalColorsToAdd, mMiniBitmap);
            mLocalColorsToAdd.clear();
        }

        private void updateSurfaceSize() {
            Trace.beginSection("ImageWallpaper#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);
            Trace.endSection();
        }

        @Override
        public boolean shouldZoomOutWallpaper() {
            return true;
        }

        @Override
        public boolean shouldWaitForEngineShown() {
            return true;
        }

        @Override
        public void onDestroy() {
            getDisplayContext().getSystemService(DisplayManager.class)
                    .unregisterDisplayListener(this);
            mMiniBitmap = null;
            mWorker.getThreadHandler().post(() -> {
                mRenderer.finish();
                mRenderer = null;
                mEglHelper.finish();
                mEglHelper = null;
            });
        return new CanvasEngine();
    }

        @Override
        public boolean supportsLocalColorExtraction() {
            return true;
        }

        @Override
        public void addLocalColorsAreas(@NonNull List<RectF> regions) {
            mWorker.getThreadHandler().post(() -> {
                if (mColorAreas.size() + mLocalColorsToAdd.size() == 0) {
                    setOffsetNotificationsEnabled(true);
                }
                Bitmap bitmap = mMiniBitmap;
                if (bitmap == null) {
                    mLocalColorsToAdd.addAll(regions);
                    if (mRenderer != null) mRenderer.use(this::updateMiniBitmapAndNotify);
                } else {
                    computeAndNotifyLocalColors(regions, bitmap);
                }
            });
        }

        private void computeAndNotifyLocalColors(@NonNull List<RectF> regions, Bitmap b) {
            List<WallpaperColors> colors = getLocalWallpaperColors(regions, b);
            mColorAreas.addAll(regions);
            try {
                notifyLocalColorsChanged(regions, colors);
            } catch (RuntimeException e) {
                Log.e(TAG, e.getMessage(), e);
            }
        }

        @Override
        public void removeLocalColorsAreas(@NonNull List<RectF> regions) {
            mWorker.getThreadHandler().post(() -> {
                mColorAreas.removeAll(regions);
                mLocalColorsToAdd.removeAll(regions);
                if (mColorAreas.size() + mLocalColorsToAdd.size() == 0) {
                    setOffsetNotificationsEnabled(false);
                }
            });
        }

        /**
         * Transform the logical coordinates into wallpaper coordinates.
         *
         * Logical coordinates are organised such that the various pages are non-overlapping. So,
         * if there are n pages, the first page will have its X coordinate on the range [0-1/n].
         *
         * The real pages are overlapping. If the Wallpaper are a width Ww and the screen a width
         * Ws, the relative width of a page Wr is Ws/Ww. This does not change if the number of
         * pages increase.
         * If there are n pages, the page k starts at the offset k * (1 - Wr) / (n - 1), as the
         * last page is at position (1-Wr) and the others are regularly spread on the range [0-
         * (1-Wr)].
         */
        private RectF pageToImgRect(RectF area) {
            if (!mDisplaySizeValid) {
                Rect window = getDisplayContext()
                        .getSystemService(WindowManager.class)
                        .getCurrentWindowMetrics()
                        .getBounds();
                mDisplayWidth = window.width();
                mDisplayHeight = window.height();
                mDisplaySizeValid = true;
            }

            // Width of a page for the caller of this API.
            float virtualPageWidth = 1f / (float) mPages;
            float leftPosOnPage = (area.left % virtualPageWidth) / virtualPageWidth;
            float rightPosOnPage = (area.right % virtualPageWidth) / virtualPageWidth;
            int currentPage = (int) Math.floor(area.centerX() / virtualPageWidth);

            RectF imgArea = new RectF();

            if (mImgWidth == 0 || mImgHeight == 0 || mDisplayWidth <= 0 || mDisplayHeight <= 0) {
                return imgArea;
            }

            imgArea.bottom = area.bottom;
            imgArea.top = area.top;

            float imageScale = Math.min(((float) mImgHeight) / mDisplayHeight, 1);
            float mappedScreenWidth = mDisplayWidth * imageScale;
            float pageWidth = Math.min(1.0f,
                    mImgWidth > 0 ? mappedScreenWidth / (float) mImgWidth : 1.f);
            float pageOffset = (1 - pageWidth) / (float) (mPages - 1);

            imgArea.left = MathUtils.constrain(
                    leftPosOnPage * pageWidth + currentPage * pageOffset, 0, 1);
            imgArea.right = MathUtils.constrain(
                    rightPosOnPage * pageWidth + currentPage * pageOffset, 0, 1);
            if (imgArea.left > imgArea.right) {
                // take full page
                imgArea.left = 0;
                imgArea.right = 1;
            }
            return imgArea;
        }

        private List<WallpaperColors> getLocalWallpaperColors(@NonNull List<RectF> areas,
                Bitmap b) {
            List<WallpaperColors> colors = new ArrayList<>(areas.size());
            for (int i = 0; i < areas.size(); i++) {
                RectF area = pageToImgRect(areas.get(i));
                if (area == null || !LOCAL_COLOR_BOUNDS.contains(area)) {
                    colors.add(null);
                    continue;
                }
                Rect subImage = new Rect(
                        (int) Math.floor(area.left * b.getWidth()),
                        (int) Math.floor(area.top * b.getHeight()),
                        (int) Math.ceil(area.right * b.getWidth()),
                        (int) Math.ceil(area.bottom * b.getHeight()));
                if (subImage.isEmpty()) {
                    // Do not notify client. treat it as too small to sample
                    colors.add(null);
                    continue;
                }
                Bitmap colorImg = Bitmap.createBitmap(b,
                        subImage.left, subImage.top, subImage.width(), subImage.height());
                WallpaperColors color = WallpaperColors.fromBitmap(colorImg);
                colors.add(color);
            }
            return colors;
        }

        @Override
        public void onSurfaceCreated(SurfaceHolder holder) {
            if (mWorker == null) return;
            mWorker.getThreadHandler().post(() -> {
                Trace.beginSection("ImageWallpaper#onSurfaceCreated");
                mEglHelper.init(holder, needSupportWideColorGamut());
                mRenderer.onSurfaceCreated();
                Trace.endSection();
            });
        }

        @Override
        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            if (mWorker == null) return;
            mWorker.getThreadHandler().post(() -> mRenderer.onSurfaceChanged(width, height));
        }

        @Override
        public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
            if (mWorker == null) return;
            mWorker.getThreadHandler().post(this::drawFrame);
        }

        private void drawFrame() {
            Trace.beginSection("ImageWallpaper#drawFrame");
            preRender();
            requestRender();
            postRender();
            Trace.endSection();
        }

        public void preRender() {
            // This method should only be invoked from worker thread.
            Trace.beginSection("ImageWallpaper#preRender");
            preRenderInternal();
            Trace.endSection();
        }

        private void preRenderInternal() {
            boolean contextRecreated = false;
            Rect frame = getSurfaceHolder().getSurfaceFrame();
            cancelFinishRenderingTask();

            // 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;
                }
            }

            // Check if we need to recreate egl surface.
            if (mEglHelper.hasEglContext() && !mEglHelper.hasEglSurface()) {
                if (!mEglHelper.createEglSurface(getSurfaceHolder(), needSupportWideColorGamut())) {
                    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());
            }
        }

        public void requestRender() {
            // This method should only be invoked from worker thread.
            Trace.beginSection("ImageWallpaper#requestRender");
            requestRenderInternal();
            Trace.endSection();
        }

        private void requestRenderInternal() {
            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);
            }
        }

        public void postRender() {
            // This method should only be invoked from worker thread.
            scheduleFinishRendering();
            reportEngineShown(false /* waitForEngineShown */);
        }

        private void cancelFinishRenderingTask() {
            if (mWorker == null) return;
            mWorker.getThreadHandler().removeCallbacks(mFinishRenderingTask);
        }

        private void scheduleFinishRendering() {
            if (mWorker == null) return;
            cancelFinishRenderingTask();
            mWorker.getThreadHandler().postDelayed(mFinishRenderingTask, DELAY_FINISH_RENDERING);
        }

        private void finishRendering() {
            Trace.beginSection("ImageWallpaper#finishRendering");
            if (mEglHelper != null) {
                mEglHelper.destroyEglSurface();
                mEglHelper.destroyEglContext();
            }
            Trace.endSection();
        }

        private boolean needSupportWideColorGamut() {
            return mRenderer.isWcgContent();
        }

        @Override
        protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
            super.dump(prefix, fd, out, args);
            out.print(prefix); out.print("Engine="); out.println(this);
            out.print(prefix); out.print("valid surface=");
            out.println(getSurfaceHolder() != null && getSurfaceHolder().getSurface() != null
                    ? getSurfaceHolder().getSurface().isValid()
                    : "null");

            out.print(prefix); out.print("surface frame=");
            out.println(getSurfaceHolder() != null ? getSurfaceHolder().getSurfaceFrame() : "null");

            mEglHelper.dump(prefix, fd, out, args);
            mRenderer.dump(prefix, fd, out, args);
        }
    }


    class CanvasEngine extends WallpaperService.Engine implements DisplayListener {
        private WallpaperManager mWallpaperManager;
        private final WallpaperLocalColorExtractor mWallpaperLocalColorExtractor;
@@ -736,13 +299,8 @@ public class ImageWallpaper extends WallpaperService {
                // be loaded, we will go into a cycle. Don't do a build where the
                // default wallpaper can't be loaded.
                Log.w(TAG, "Unable to load wallpaper!", exception);
                try {
                    mWallpaperManager.clear(WallpaperManager.FLAG_SYSTEM);
                } catch (IOException ex) {
                    // now we're really screwed.
                    Log.w(TAG, "Unable reset to default wallpaper!", ex);
                }

                mWallpaperManager.clearWallpaper(
                        WallpaperManager.FLAG_SYSTEM, UserHandle.USER_CURRENT);
                try {
                    bitmap = mWallpaperManager.getBitmapAsUser(UserHandle.USER_CURRENT, false);
                } catch (RuntimeException | OutOfMemoryError e) {
@@ -868,7 +426,6 @@ public class ImageWallpaper extends WallpaperService {
            mWallpaperLocalColorExtractor.setDisplayDimensions(window.width(), window.height());
        }


        @Override
        protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
            super.dump(prefix, fd, out, args);
Loading