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

Commit 656d7741 authored by Galia Peycheva's avatar Galia Peycheva Committed by Android (Google) Code Review
Browse files

Merge "Fix synchronisation in BackgroundBlurDrawable" into sc-dev

parents 065fa500 d129f1e0
Loading
Loading
Loading
Loading
+0 −60
Original line number Diff line number Diff line
@@ -3504,64 +3504,4 @@ public final class SurfaceControl implements Parcelable {
    public static Transaction getGlobalTransaction() {
        return sGlobalTransaction;
    }

    /**
     * Wrapper for sending blur data to SurfaceFlinger.
     * @hide
     */
    public static final class BlurRegion {
        public int blurRadius;
        public float cornerRadiusTL;
        public float cornerRadiusTR;
        public float cornerRadiusBL;
        public float cornerRadiusBR;
        public float alpha = 1;
        public boolean visible = true;
        public final Rect rect = new Rect();

        private final float[] mFloatArray = new float[10];

        public BlurRegion() {
        }

        public BlurRegion(BlurRegion other) {
            rect.set(other.rect);
            blurRadius = other.blurRadius;
            alpha = other.alpha;
            cornerRadiusTL = other.cornerRadiusTL;
            cornerRadiusTR = other.cornerRadiusTR;
            cornerRadiusBL = other.cornerRadiusBL;
            cornerRadiusBR = other.cornerRadiusBR;
        }

        /**
         * Serializes this class into a float array that's more JNI friendly.
         */
        public float[] toFloatArray() {
            mFloatArray[0] = blurRadius;
            mFloatArray[1] = alpha;
            mFloatArray[2] = rect.left;
            mFloatArray[3] = rect.top;
            mFloatArray[4] = rect.right;
            mFloatArray[5] = rect.bottom;
            mFloatArray[6] = cornerRadiusTL;
            mFloatArray[7] = cornerRadiusTR;
            mFloatArray[8] = cornerRadiusBL;
            mFloatArray[9] = cornerRadiusBR;
            return mFloatArray;
        }

        @Override
        public String toString() {
            return "BlurRegion{"
                    + "blurRadius=" + blurRadius
                    + ", corners={" + cornerRadiusTL
                    + "," + cornerRadiusTR
                    + "," + cornerRadiusBL
                    + "," + cornerRadiusBR
                    + "}, alpha=" + alpha
                    + ", rect=" + rect
                    + "}";
        }
    }
}
+13 −8
Original line number Diff line number Diff line
@@ -3969,11 +3969,12 @@ public final class ViewRootImpl implements ViewParent,
    }

    private void addFrameCallbackIfNeeded() {
        boolean nextDrawUseBlastSync = mNextDrawUseBlastSync;
        boolean hasBlur = mBlurRegionAggregator.hasRegions();
        boolean reportNextDraw = mReportNextDraw;
        final boolean nextDrawUseBlastSync = mNextDrawUseBlastSync;
        final boolean reportNextDraw = mReportNextDraw;
        final boolean hasBlurUpdates = mBlurRegionAggregator.hasUpdates();
        final boolean needsCallbackForBlur = hasBlurUpdates || mBlurRegionAggregator.hasRegions();

        if (!nextDrawUseBlastSync && !reportNextDraw && !hasBlur) {
        if (!nextDrawUseBlastSync && !reportNextDraw && !needsCallbackForBlur) {
            return;
        }

@@ -3981,18 +3982,22 @@ public final class ViewRootImpl implements ViewParent,
            Log.d(mTag, "Creating frameDrawingCallback"
                    + " nextDrawUseBlastSync=" + nextDrawUseBlastSync
                    + " reportNextDraw=" + reportNextDraw
                    + " hasBlur=" + hasBlur);
                    + " hasBlurUpdates=" + hasBlurUpdates);
        }

        // The callback will run on a worker thread pool from the render thread.
        final BackgroundBlurDrawable.BlurRegion[] blurRegionsForFrame =
                needsCallbackForBlur ?  mBlurRegionAggregator.getBlurRegionsCopyForRT() : null;

        // The callback will run on the render thread.
        HardwareRenderer.FrameDrawingCallback frameDrawingCallback = frame -> {
            if (DEBUG_BLAST) {
                Log.d(mTag, "Received frameDrawingCallback frameNum=" + frame + "."
                        + " Creating transactionCompleteCallback=" + nextDrawUseBlastSync);
            }

            if (hasBlur) {
                mBlurRegionAggregator.dispatchBlurTransactionIfNeeded(frame);
            if (needsCallbackForBlur) {
                mBlurRegionAggregator
                    .dispatchBlurTransactionIfNeeded(frame, blurRegionsForFrame, hasBlurUpdates);
            }

            if (mBlastBufferQueue == null) {
+242 −69
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.internal.graphics.drawable;
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UiThread;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -31,12 +32,14 @@ import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RenderNode;
import android.graphics.drawable.Drawable;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.view.SurfaceControl;
import android.util.LongSparseArray;
import android.view.ViewRootImpl;

import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;

/**
 * A drawable that keeps track of a blur region, pokes a hole under it, and propagates its state
@@ -52,26 +55,40 @@ public final class BackgroundBlurDrawable extends Drawable {
    private final Paint mPaint = new Paint();
    private final Path mRectPath = new Path();
    private final float[] mTmpRadii = new float[8];
    private final SurfaceControl.BlurRegion mBlurRegion = new SurfaceControl.BlurRegion();

    // This will be called from a thread pool.
    private final RenderNode.PositionUpdateListener mPositionUpdateListener =
    private boolean mVisible = true;

    // Confined to UiThread. The values are copied into a BlurRegion, which lives on
    // RenderThread to avoid interference with UiThread updates.
    private int mBlurRadius;
    private float mCornerRadiusTL;
    private float mCornerRadiusTR;
    private float mCornerRadiusBL;
    private float mCornerRadiusBR;
    private float mAlpha = 1;

    // Do not update from UiThread. This holds the latest position for this drawable. It is used
    // by the Aggregator from RenderThread to get the final position of the blur region sent to SF
    private final Rect mRect = new Rect();
    // This is called from a thread pool. The callbacks might come out of order w.r.t. the frame
    // number, so we send a Runnable holding the actual update to the Aggregator. The Aggregator
    // can apply the update on RenderThread when processing that same frame.
    @VisibleForTesting
    public final RenderNode.PositionUpdateListener mPositionUpdateListener =
            new RenderNode.PositionUpdateListener() {
            @Override
            public void positionChanged(long frameNumber, int left, int top, int right,
                    int bottom) {
                synchronized (mAggregator) {
                    mBlurRegion.rect.set(left, top, right, bottom);
                    mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion);
                }
                mAggregator.onRenderNodePositionChanged(frameNumber, () -> {
                    mRect.set(left, top, right, bottom);
                });
            }

            @Override
            public void positionLost(long frameNumber) {
                synchronized (mAggregator) {
                    mBlurRegion.rect.setEmpty();
                    mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion);
                }
                mAggregator.onRenderNodePositionChanged(frameNumber, () -> {
                    mRect.setEmpty();
                });
            }
        };

@@ -79,6 +96,7 @@ public final class BackgroundBlurDrawable extends Drawable {
        mAggregator = aggregator;
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
        mPaint.setColor(Color.TRANSPARENT);
        mPaint.setAntiAlias(true);
        mRenderNode = new RenderNode("BackgroundBlurDrawable");
        mRenderNode.addPositionUpdateListener(mPositionUpdateListener);
    }
@@ -104,23 +122,30 @@ public final class BackgroundBlurDrawable extends Drawable {
    public boolean setVisible(boolean visible, boolean restart) {
        boolean changed = super.setVisible(visible, restart);
        if (changed) {
            mBlurRegion.visible = visible;
            mVisible = visible;
            mAggregator.onBlurDrawableUpdated(this);
        }
        return changed;
    }

    @Override
    public void setAlpha(int alpha) {
        mBlurRegion.alpha = alpha / 255f;
        if (mAlpha != alpha / 255f) {
            mAlpha = alpha / 255f;
            invalidateSelf();
            mAggregator.onBlurDrawableUpdated(this);
        }
    }

    /**
     * Blur radius in pixels.
     */
    public void setBlurRadius(int blurRadius) {
        mBlurRegion.blurRadius = blurRadius;
        if (mBlurRadius != blurRadius) {
            mBlurRadius = blurRadius;
            invalidateSelf();
            mAggregator.onBlurDrawableUpdated(this);
        }
    }

    /**
@@ -139,14 +164,18 @@ public final class BackgroundBlurDrawable extends Drawable {
     */
    public void setCornerRadius(float cornerRadiusTL, float cornerRadiusTR, float cornerRadiusBL,
            float cornerRadiusBR) {
        synchronized (mAggregator) {
            mBlurRegion.cornerRadiusTL = cornerRadiusTL;
            mBlurRegion.cornerRadiusTR = cornerRadiusTR;
            mBlurRegion.cornerRadiusBL = cornerRadiusBL;
            mBlurRegion.cornerRadiusBR = cornerRadiusBR;
        }
        if (mCornerRadiusTL != cornerRadiusTL
                || mCornerRadiusTR != cornerRadiusTR
                || mCornerRadiusBL != cornerRadiusBL
                || mCornerRadiusBR != cornerRadiusBR) {
            mCornerRadiusTL = cornerRadiusTL;
            mCornerRadiusTR = cornerRadiusTR;
            mCornerRadiusBL = cornerRadiusBL;
            mCornerRadiusBR = cornerRadiusBR;
            updatePath();
            invalidateSelf();
            mAggregator.onBlurDrawableUpdated(this);
        }
    }

    @Override
@@ -157,12 +186,10 @@ public final class BackgroundBlurDrawable extends Drawable {
    }

    private void updatePath() {
        synchronized (mAggregator) {
            mTmpRadii[0] = mTmpRadii[1] = mBlurRegion.cornerRadiusTL;
            mTmpRadii[2] = mTmpRadii[3] = mBlurRegion.cornerRadiusTR;
            mTmpRadii[4] = mTmpRadii[5] = mBlurRegion.cornerRadiusBL;
            mTmpRadii[6] = mTmpRadii[7] = mBlurRegion.cornerRadiusBR;
        }
        mTmpRadii[0] = mTmpRadii[1] = mCornerRadiusTL;
        mTmpRadii[2] = mTmpRadii[3] = mCornerRadiusTR;
        mTmpRadii[4] = mTmpRadii[5] = mCornerRadiusBL;
        mTmpRadii[6] = mTmpRadii[7] = mCornerRadiusBR;
        mRectPath.reset();
        if (getAlpha() == 0 || !isVisible()) {
            return;
@@ -182,17 +209,32 @@ public final class BackgroundBlurDrawable extends Drawable {
        return PixelFormat.TRANSLUCENT;
    }

    @Override
    public String toString() {
        return "BackgroundBlurDrawable{"
            + "blurRadius=" + mBlurRadius
            + ", corners={" + mCornerRadiusTL
            + "," + mCornerRadiusTR
            + "," + mCornerRadiusBL
            + "," + mCornerRadiusBR
            + "}, alpha=" + mAlpha
            + ", visible=" + mVisible
            + "}";
    }

    /**
     * Responsible for keeping track of all blur regions of a {@link ViewRootImpl} and posting a
     * message when it's time to propagate them.
     */
    public static final class Aggregator {

        private final ArrayMap<BackgroundBlurDrawable, SurfaceControl.BlurRegion> mBlurRegions =
                new ArrayMap<>();
        private final Object mRtLock = new Object();
        // Maintains a list of all *visible* blur drawables. Confined to  UI thread
        private final ArraySet<BackgroundBlurDrawable> mDrawables = new ArraySet();
        @GuardedBy("mRtLock")
        private final LongSparseArray<ArraySet<Runnable>> mFrameRtUpdates = new LongSparseArray();
        private final ViewRootImpl mViewRoot;
        private float[][] mTmpBlurRegionsArray;
        private boolean mNeedsUpdate;
        private BlurRegion[] mTmpBlurRegionsForFrame = new BlurRegion[0];
        private boolean mHasUiUpdates;

        public Aggregator(ViewRootImpl viewRoot) {
            mViewRoot = viewRoot;
@@ -209,60 +251,191 @@ public final class BackgroundBlurDrawable extends Drawable {
        }

        /**
         * Called from RenderThread only, already locked.
         * @param drawable
         * @param blurRegion
         * Called when a BackgroundBlurDrawable has been updated
         */
        void onBlurRegionUpdated(BackgroundBlurDrawable drawable,
                SurfaceControl.BlurRegion blurRegion) {
            if (blurRegion.rect.isEmpty() || blurRegion.alpha == 0 || blurRegion.blurRadius == 0
                    || !blurRegion.visible) {
                mBlurRegions.remove(drawable);
                mNeedsUpdate = true;
        @UiThread
        void onBlurDrawableUpdated(BackgroundBlurDrawable drawable) {
            final boolean shouldBeDrawn =
                    drawable.mAlpha != 0 && drawable.mBlurRadius > 0 && drawable.mVisible;
            final boolean isDrawn = mDrawables.contains(drawable);
            if (shouldBeDrawn) {
                mHasUiUpdates = true;
                if (!isDrawn) {
                    mDrawables.add(drawable);
                    if (DEBUG) {
                    Log.d(TAG, "Remove " + blurRegion);
                        Log.d(TAG, "Add " + drawable);
                    }
                } else {
                mBlurRegions.put(drawable, blurRegion);
                mNeedsUpdate = true;
                    if (DEBUG) {
                    Log.d(TAG, "Update " + blurRegion);
                        Log.d(TAG, "Update " + drawable);
                    }
                }
            } else if (!shouldBeDrawn && isDrawn) {
                mHasUiUpdates = true;
                mDrawables.remove(drawable);
                if (DEBUG) {
                    Log.d(TAG, "Remove " + drawable);
                }
            }
        }

        // Called from a thread pool
        void onRenderNodePositionChanged(long frameNumber, Runnable update) {
            // One of the blur region's position has changed, so we have to send an updated list
            // of blur regions to SurfaceFlinger for this frame.
            synchronized (mRtLock) {
                ArraySet<Runnable> frameRtUpdates = mFrameRtUpdates.get(frameNumber);
                if (frameRtUpdates == null) {
                    frameRtUpdates = new ArraySet<>();
                    mFrameRtUpdates.put(frameNumber, frameRtUpdates);
                }
                frameRtUpdates.add(update);
            }
        }

        /**
         * @return true if there are any updates that need to be sent to SF
         */
        @UiThread
        public boolean hasUpdates() {
            return mHasUiUpdates;
        }

        /**
         * If there are any blur regions visible on the screen at the moment.
         * @return true if there are any visible blur regions
         */
        @UiThread
        public boolean hasRegions() {
            return mBlurRegions.size() > 0;
            return mDrawables.size() > 0;
        }

        /**
         * Dispatch blur updates, if there were any.
         * @param frameNumber Frame where the update should happen.
         * @return an array of BlurRegions, which are holding a copy of the information in
         *         all the currently visible BackgroundBlurDrawables
         */
        public void dispatchBlurTransactionIfNeeded(long frameNumber) {
            synchronized (this) {
                if (!mNeedsUpdate) {
                    return;
        @UiThread
        public BlurRegion[] getBlurRegionsCopyForRT() {
            if (mHasUiUpdates) {
                mTmpBlurRegionsForFrame = new BlurRegion[mDrawables.size()];
                for (int i = 0; i < mDrawables.size(); i++) {
                    mTmpBlurRegionsForFrame[i] = new BlurRegion(mDrawables.valueAt(i));
                }
                mHasUiUpdates = false;
            }

            return mTmpBlurRegionsForFrame;
        }
                mNeedsUpdate = false;

                if (mTmpBlurRegionsArray == null
                        || mTmpBlurRegionsArray.length != mBlurRegions.size()) {
                    mTmpBlurRegionsArray = new float[mBlurRegions.size()][];
        /**
         * Called on RenderThread.
         *
         * @return all blur regions if there are any ui or position updates for this frame,
         *         null otherwise
         */
        @VisibleForTesting
        public float[][] getBlurRegionsToDispatchToSf(long frameNumber,
                BlurRegion[] blurRegionsForFrame, boolean hasUiUpdatesForFrame) {
            synchronized (mRtLock) {
                if (!hasUiUpdatesForFrame && (mFrameRtUpdates.size() == 0
                            || mFrameRtUpdates.keyAt(0) > frameNumber)) {
                    return null;
                }

                // mFrameRtUpdates holds position updates coming from a thread pool span from
                // RenderThread. At this point, all position updates for frame frameNumber should
                // have been added to mFrameRtUpdates.
                // Here, we apply all updates for frames <= frameNumber in case some previous update
                // has been missed. This also protects mFrameRtUpdates from memory leaks.
                while (mFrameRtUpdates.size() != 0 && mFrameRtUpdates.keyAt(0) <= frameNumber) {
                    final ArraySet<Runnable> frameUpdates = mFrameRtUpdates.valueAt(0);
                    mFrameRtUpdates.removeAt(0);
                    for (int i = 0; i < frameUpdates.size(); i++) {
                        frameUpdates.valueAt(i).run();
                    }
                }
            }

            if (DEBUG) {
                Log.d(TAG, "Dispatching " + blurRegionsForFrame.length + " blur regions:");
            }

            final float[][] blurRegionsArray = new float[blurRegionsForFrame.length][];
            for (int i = 0; i < blurRegionsArray.length; i++) {
                blurRegionsArray[i] = blurRegionsForFrame[i].toFloatArray();
                if (DEBUG) {
                    Log.d(TAG, "onBlurRegionUpdated will dispatch " + mTmpBlurRegionsArray.length
                            + " regions for frame " + frameNumber);
                    Log.d(TAG, blurRegionsForFrame[i].toString());
                }
            }
            return blurRegionsArray;
        }

        /**
         * Called on RenderThread in FrameDrawingCallback.
         * Dispatch all blur regions if there are any ui or position updates.
         */
        public void dispatchBlurTransactionIfNeeded(long frameNumber,
                BlurRegion[] blurRegionsForFrame, boolean hasUiUpdatesForFrame) {
            final float[][] blurRegionsArray = getBlurRegionsToDispatchToSf(frameNumber,
                    blurRegionsForFrame, hasUiUpdatesForFrame);
            if (blurRegionsArray != null) {
                mViewRoot.dispatchBlurRegions(blurRegionsArray, frameNumber);
            }
        }
                for (int i = 0; i < mTmpBlurRegionsArray.length; i++) {
                    mTmpBlurRegionsArray[i] = mBlurRegions.valueAt(i).toFloatArray();

    }

    /**
     * Wrapper for sending blur data to SurfaceFlinger
     * Confined to RenderThread.
     */
    public static final class BlurRegion {
        public final int blurRadius;
        public final float cornerRadiusTL;
        public final float cornerRadiusTR;
        public final float cornerRadiusBL;
        public final float cornerRadiusBR;
        public final float alpha;
        public final Rect rect;

        BlurRegion(BackgroundBlurDrawable drawable) {
            alpha = drawable.mAlpha;
            blurRadius = drawable.mBlurRadius;
            cornerRadiusTL = drawable.mCornerRadiusTL;
            cornerRadiusTR = drawable.mCornerRadiusTR;
            cornerRadiusBL = drawable.mCornerRadiusBL;
            cornerRadiusBR = drawable.mCornerRadiusBR;
            rect = drawable.mRect;
        }

                mViewRoot.dispatchBlurRegions(mTmpBlurRegionsArray, frameNumber);
        /**
         * Serializes this class into a float array that's more JNI friendly.
         */
        float[] toFloatArray() {
            final float[] floatArray = new float[10];
            floatArray[0] = blurRadius;
            floatArray[1] = alpha;
            floatArray[2] = rect.left;
            floatArray[3] = rect.top;
            floatArray[4] = rect.right;
            floatArray[5] = rect.bottom;
            floatArray[6] = cornerRadiusTL;
            floatArray[7] = cornerRadiusTR;
            floatArray[8] = cornerRadiusBL;
            floatArray[9] = cornerRadiusBR;
            return floatArray;
        }

        @Override
        public String toString() {
            return "BlurRegion{"
                    + "blurRadius=" + blurRadius
                    + ", corners={" + cornerRadiusTL
                    + "," + cornerRadiusTR
                    + "," + cornerRadiusBL
                    + "," + cornerRadiusBR
                    + "}, alpha=" + alpha
                    + ", rect=" + rect
                    + "}";
        }
    }
}
+318 −0

File added.

Preview size limit exceeded, changes collapsed.