Loading core/java/android/view/SurfaceControl.java +0 −60 Original line number Diff line number Diff line Loading @@ -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 + "}"; } } } core/java/android/view/ViewRootImpl.java +13 −8 Original line number Diff line number Diff line Loading @@ -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; } Loading @@ -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) { Loading core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java +242 −69 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading @@ -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(); }); } }; Loading @@ -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); } Loading @@ -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); } } /** Loading @@ -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 Loading @@ -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; Loading @@ -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; Loading @@ -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 + "}"; } } } core/tests/coretests/src/android/view/BlurAggregatorTest.java 0 → 100644 +318 −0 File added.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/view/SurfaceControl.java +0 −60 Original line number Diff line number Diff line Loading @@ -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 + "}"; } } }
core/java/android/view/ViewRootImpl.java +13 −8 Original line number Diff line number Diff line Loading @@ -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; } Loading @@ -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) { Loading
core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java +242 −69 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading @@ -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(); }); } }; Loading @@ -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); } Loading @@ -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); } } /** Loading @@ -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 Loading @@ -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; Loading @@ -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; Loading @@ -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 + "}"; } } }
core/tests/coretests/src/android/view/BlurAggregatorTest.java 0 → 100644 +318 −0 File added.Preview size limit exceeded, changes collapsed. Show changes