Merge "Fix synchronisation in BackgroundBlurDrawable" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
656d774184
@@ -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
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
invalidateSelf();
|
||||
if (mAlpha != alpha / 255f) {
|
||||
mAlpha = alpha / 255f;
|
||||
invalidateSelf();
|
||||
mAggregator.onBlurDrawableUpdated(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Blur radius in pixels.
|
||||
*/
|
||||
public void setBlurRadius(int blurRadius) {
|
||||
mBlurRegion.blurRadius = blurRadius;
|
||||
invalidateSelf();
|
||||
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);
|
||||
}
|
||||
updatePath();
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@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;
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Remove " + blurRegion);
|
||||
@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, "Add " + drawable);
|
||||
}
|
||||
} else {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Update " + drawable);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mBlurRegions.put(drawable, blurRegion);
|
||||
mNeedsUpdate = true;
|
||||
} else if (!shouldBeDrawn && isDrawn) {
|
||||
mHasUiUpdates = true;
|
||||
mDrawables.remove(drawable);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Update " + blurRegion);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If there are any blur regions visible on the screen at the moment.
|
||||
* @return true if there are any updates that need to be sent to SF
|
||||
*/
|
||||
@UiThread
|
||||
public boolean hasUpdates() {
|
||||
return mHasUiUpdates;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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));
|
||||
}
|
||||
mNeedsUpdate = false;
|
||||
|
||||
if (mTmpBlurRegionsArray == null
|
||||
|| mTmpBlurRegionsArray.length != mBlurRegions.size()) {
|
||||
mTmpBlurRegionsArray = new float[mBlurRegions.size()][];
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onBlurRegionUpdated will dispatch " + mTmpBlurRegionsArray.length
|
||||
+ " regions for frame " + frameNumber);
|
||||
}
|
||||
for (int i = 0; i < mTmpBlurRegionsArray.length; i++) {
|
||||
mTmpBlurRegionsArray[i] = mBlurRegions.valueAt(i).toFloatArray();
|
||||
}
|
||||
|
||||
mViewRoot.dispatchBlurRegions(mTmpBlurRegionsArray, frameNumber);
|
||||
mHasUiUpdates = false;
|
||||
}
|
||||
|
||||
return mTmpBlurRegionsForFrame;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
core/tests/coretests/src/android/view/BlurAggregatorTest.java
Normal file
318
core/tests/coretests/src/android/view/BlurAggregatorTest.java
Normal file
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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 android.view;
|
||||
|
||||
import static androidx.test.InstrumentationRegistry.getInstrumentation;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static junit.framework.TestCase.assertNotNull;
|
||||
import static junit.framework.TestCase.assertNull;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
|
||||
import android.content.Context;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
|
||||
import com.android.internal.graphics.drawable.BackgroundBlurDrawable.Aggregator;
|
||||
import com.android.internal.graphics.drawable.BackgroundBlurDrawable.BlurRegion;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@Presubmit
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class BlurAggregatorTest {
|
||||
private static final int TEST_BLUR_RADIUS = 30;
|
||||
private static final int TEST_FRAME_NUMBER = 1;
|
||||
|
||||
private Context mContext;
|
||||
|
||||
private Aggregator mAggregator;
|
||||
private BackgroundBlurDrawable mDrawable;
|
||||
|
||||
private ViewRootImpl mViewRoot;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = getInstrumentation().getTargetContext();
|
||||
getInstrumentation().runOnMainSync(() -> {
|
||||
mViewRoot = new ViewRootImpl(mContext, mContext.getDisplayNoVerify());
|
||||
});
|
||||
mAggregator = new Aggregator(mViewRoot);
|
||||
mDrawable = createTestBackgroundBlurDrawable();
|
||||
}
|
||||
|
||||
private BackgroundBlurDrawable createTestBackgroundBlurDrawable() {
|
||||
final BackgroundBlurDrawable drawable = mAggregator.createBackgroundBlurDrawable(mContext);
|
||||
drawable.setBlurRadius(TEST_BLUR_RADIUS);
|
||||
final boolean hasUpdates = mAggregator.hasUpdates();
|
||||
final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
|
||||
mAggregator.getBlurRegionsToDispatchToSf(TEST_FRAME_NUMBER, blurRegions, hasUpdates);
|
||||
return drawable;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlurRadiusUpdatePropagatesToRenderThreadIfNeeded() {
|
||||
mDrawable.setBlurRadius(TEST_BLUR_RADIUS);
|
||||
assertFalse(mAggregator.hasUpdates());
|
||||
|
||||
mDrawable.setBlurRadius(0);
|
||||
assertTrue(mAggregator.hasUpdates());
|
||||
BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
|
||||
assertEquals(0, blurRegions.length);
|
||||
assertFalse(mAggregator.hasUpdates());
|
||||
|
||||
mDrawable.setBlurRadius(TEST_BLUR_RADIUS);
|
||||
assertTrue(mAggregator.hasUpdates());
|
||||
blurRegions = mAggregator.getBlurRegionsCopyForRT();
|
||||
assertEquals(1, blurRegions.length);
|
||||
assertEquals(TEST_BLUR_RADIUS, blurRegions[0].blurRadius);
|
||||
assertFalse(mAggregator.hasUpdates());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAlphaUpdatePropagatesToRenderThreadIfNeeded() {
|
||||
mDrawable.setAlpha(20);
|
||||
assertTrue(mAggregator.hasUpdates());
|
||||
BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
|
||||
assertEquals(1, blurRegions.length);
|
||||
assertEquals(20 / 255f, blurRegions[0].alpha);
|
||||
assertFalse(mAggregator.hasUpdates());
|
||||
|
||||
mDrawable.setAlpha(20);
|
||||
assertFalse(mAggregator.hasUpdates());
|
||||
|
||||
mDrawable.setAlpha(0);
|
||||
assertTrue(mAggregator.hasUpdates());
|
||||
blurRegions = mAggregator.getBlurRegionsCopyForRT();
|
||||
assertEquals(0, blurRegions.length);
|
||||
assertFalse(mAggregator.hasUpdates());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCornerRadiusUpdatePropagatesToRenderThreadIfNeeded() {
|
||||
mDrawable.setCornerRadius(1f, 2f, 3f, 4f);
|
||||
assertTrue(mAggregator.hasUpdates());
|
||||
final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
|
||||
assertEquals(1, blurRegions.length);
|
||||
assertEquals(1f, blurRegions[0].cornerRadiusTL);
|
||||
assertEquals(2f, blurRegions[0].cornerRadiusTR);
|
||||
assertEquals(3f, blurRegions[0].cornerRadiusBL);
|
||||
assertEquals(4f, blurRegions[0].cornerRadiusBR);
|
||||
assertFalse(mAggregator.hasUpdates());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVisibleUpdatePropagatesToRenderThreadIfNeeded() {
|
||||
mDrawable.setVisible(false, /* restart= */false);
|
||||
assertTrue(mAggregator.hasUpdates());
|
||||
BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
|
||||
assertEquals(0, blurRegions.length);
|
||||
assertFalse(mAggregator.hasUpdates());
|
||||
|
||||
mDrawable.setVisible(true, /* restart= */ false);
|
||||
assertTrue(mAggregator.hasUpdates());
|
||||
blurRegions = mAggregator.getBlurRegionsCopyForRT();
|
||||
assertEquals(1, blurRegions.length);
|
||||
assertEquals(TEST_BLUR_RADIUS, blurRegions[0].blurRadius);
|
||||
assertFalse(mAggregator.hasUpdates());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlurRegionCopyForRtIsSameIfNoUiUpdates() {
|
||||
mDrawable.setBlurRadius(30);
|
||||
BlurRegion[] blurRegions1 = mAggregator.getBlurRegionsCopyForRT();
|
||||
assertEquals(1, blurRegions1.length);
|
||||
assertEquals(30, blurRegions1[0].blurRadius);
|
||||
|
||||
BlurRegion[] blurRegions2 = mAggregator.getBlurRegionsCopyForRT();
|
||||
assertEquals(blurRegions1, blurRegions2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPositionUpdateAppearsInBlurRegion() {
|
||||
BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
|
||||
assertEquals(1, blurRegions.length);
|
||||
|
||||
mDrawable.mPositionUpdateListener.positionChanged(TEST_FRAME_NUMBER, 1, 2, 3, 4);
|
||||
mAggregator.getBlurRegionsToDispatchToSf(TEST_FRAME_NUMBER, blurRegions,
|
||||
mAggregator.hasUpdates());
|
||||
assertEquals(1, blurRegions[0].rect.left);
|
||||
assertEquals(2, blurRegions[0].rect.top);
|
||||
assertEquals(3, blurRegions[0].rect.right);
|
||||
assertEquals(4, blurRegions[0].rect.bottom);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoBlurRegionsDispatchedWhenNoUpdates() {
|
||||
final boolean hasUpdates = mAggregator.hasUpdates();
|
||||
assertFalse(hasUpdates);
|
||||
final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
|
||||
assertEquals(1, blurRegions.length);
|
||||
|
||||
float[][] blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf(
|
||||
TEST_FRAME_NUMBER, blurRegions, hasUpdates);
|
||||
assertNull(blurRegionsForSf);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlurRegionDispatchedIfOnlyDrawableUpdated() {
|
||||
mDrawable.setBlurRadius(50);
|
||||
final boolean hasUpdates = mAggregator.hasUpdates();
|
||||
assertTrue(hasUpdates);
|
||||
final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
|
||||
assertEquals(1, blurRegions.length);
|
||||
|
||||
float[][] blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf(
|
||||
TEST_FRAME_NUMBER, blurRegions, hasUpdates);
|
||||
assertNotNull(blurRegionsForSf);
|
||||
assertEquals(1, blurRegionsForSf.length);
|
||||
assertEquals(50f, blurRegionsForSf[0][0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlurRegionDispatchedIfOnlyPositionUpdated() {
|
||||
final boolean hasUpdates = mAggregator.hasUpdates();
|
||||
assertFalse(hasUpdates);
|
||||
final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
|
||||
assertEquals(1, blurRegions.length);
|
||||
|
||||
mDrawable.mPositionUpdateListener.positionChanged(TEST_FRAME_NUMBER, 1, 2, 3, 4);
|
||||
float[][] blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf(
|
||||
TEST_FRAME_NUMBER, blurRegions, hasUpdates);
|
||||
assertNotNull(blurRegionsForSf);
|
||||
assertEquals(1, blurRegionsForSf.length);
|
||||
assertEquals((float) TEST_BLUR_RADIUS, blurRegionsForSf[0][0]);
|
||||
assertEquals(1f, blurRegionsForSf[0][2]);
|
||||
assertEquals(2f, blurRegionsForSf[0][3]);
|
||||
assertEquals(3f, blurRegionsForSf[0][4]);
|
||||
assertEquals(4f, blurRegionsForSf[0][5]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPositionUpdateIsAppliedInNextFrameIfMissed() {
|
||||
final boolean hasUpdates = mAggregator.hasUpdates();
|
||||
assertFalse(hasUpdates);
|
||||
final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
|
||||
assertEquals(1, blurRegions.length);
|
||||
|
||||
mDrawable.mPositionUpdateListener.positionChanged(TEST_FRAME_NUMBER, 1, 2, 3, 4);
|
||||
float[][] blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf(
|
||||
TEST_FRAME_NUMBER + 1, blurRegions, hasUpdates);
|
||||
assertNotNull(blurRegionsForSf);
|
||||
assertEquals(1, blurRegionsForSf.length);
|
||||
assertEquals((float) TEST_BLUR_RADIUS, blurRegionsForSf[0][0]);
|
||||
assertEquals(1f, blurRegionsForSf[0][2]);
|
||||
assertEquals(2f, blurRegionsForSf[0][3]);
|
||||
assertEquals(3f, blurRegionsForSf[0][4]);
|
||||
assertEquals(4f, blurRegionsForSf[0][5]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleDrawablesDispatchedToSfIfOneIsUpdated() {
|
||||
final BackgroundBlurDrawable drawable2 = createTestBackgroundBlurDrawable();
|
||||
drawable2.setBlurRadius(50);
|
||||
final boolean hasUpdates = mAggregator.hasUpdates();
|
||||
assertTrue(hasUpdates);
|
||||
final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
|
||||
assertEquals(2, blurRegions.length);
|
||||
|
||||
// Check that an update in one of the drawables triggers a dispatch of all blur regions
|
||||
float[][] blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf(
|
||||
TEST_FRAME_NUMBER, blurRegions, hasUpdates);
|
||||
assertNotNull(blurRegionsForSf);
|
||||
assertEquals(2, blurRegionsForSf.length);
|
||||
|
||||
// Check that the Aggregator deleted all position updates for frame TEST_FRAME_NUMBER
|
||||
blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf(
|
||||
TEST_FRAME_NUMBER, blurRegions, /* hasUiUpdates= */ false);
|
||||
assertNull(blurRegionsForSf);
|
||||
|
||||
// Check that a position update triggers a dispatch of all blur regions
|
||||
drawable2.mPositionUpdateListener.positionChanged(TEST_FRAME_NUMBER, 1, 2, 3, 4);
|
||||
blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf(
|
||||
TEST_FRAME_NUMBER + 1, blurRegions, hasUpdates);
|
||||
assertNotNull(blurRegionsForSf);
|
||||
assertEquals(2, blurRegionsForSf.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUiThreadUpdatesDoNotChangeStateOnRenderThread() {
|
||||
// Updates for frame N
|
||||
mDrawable.setBlurRadius(50);
|
||||
mDrawable.setCornerRadius(1, 2, 3, 4);
|
||||
mDrawable.setAlpha(20);
|
||||
|
||||
final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
|
||||
assertEquals(1, blurRegions.length);
|
||||
assertEquals(50, blurRegions[0].blurRadius);
|
||||
assertEquals(20 / 255f, blurRegions[0].alpha);
|
||||
assertEquals(1f, blurRegions[0].cornerRadiusTL);
|
||||
assertEquals(2f, blurRegions[0].cornerRadiusTR);
|
||||
assertEquals(3f, blurRegions[0].cornerRadiusBL);
|
||||
assertEquals(4f, blurRegions[0].cornerRadiusBR);
|
||||
|
||||
// Updates for frame N+1
|
||||
mDrawable.setBlurRadius(60);
|
||||
mDrawable.setCornerRadius(10, 20, 30, 40);
|
||||
mDrawable.setAlpha(40);
|
||||
|
||||
// Assert state for frame N is untouched
|
||||
assertEquals(50, blurRegions[0].blurRadius);
|
||||
assertEquals(20 / 255f, blurRegions[0].alpha);
|
||||
assertEquals(1f, blurRegions[0].cornerRadiusTL);
|
||||
assertEquals(2f, blurRegions[0].cornerRadiusTR);
|
||||
assertEquals(3f, blurRegions[0].cornerRadiusBL);
|
||||
assertEquals(4f, blurRegions[0].cornerRadiusBR);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPositionUpdatesForFutureFramesAreNotAppliedForCurrentFrame() {
|
||||
final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
|
||||
|
||||
mDrawable.mPositionUpdateListener.positionChanged(TEST_FRAME_NUMBER, 1, 2, 3, 4);
|
||||
mDrawable.mPositionUpdateListener.positionChanged(TEST_FRAME_NUMBER + 1, 5, 6, 7, 8);
|
||||
|
||||
final float[][] blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf(
|
||||
TEST_FRAME_NUMBER, blurRegions, /* hasUiUpdates= */ false);
|
||||
assertNotNull(blurRegionsForSf);
|
||||
assertEquals(1, blurRegionsForSf.length);
|
||||
// Assert state for first frame is not affected by update for second frame
|
||||
assertEquals((float) TEST_BLUR_RADIUS, blurRegionsForSf[0][0]);
|
||||
assertEquals(1f, blurRegionsForSf[0][2]);
|
||||
assertEquals(2f, blurRegionsForSf[0][3]);
|
||||
assertEquals(3f, blurRegionsForSf[0][4]);
|
||||
assertEquals(4f, blurRegionsForSf[0][5]);
|
||||
|
||||
final float[][] blurRegionsForSfForNextFrame = mAggregator.getBlurRegionsToDispatchToSf(
|
||||
TEST_FRAME_NUMBER + 1, blurRegions, /* hasUiUpdates= */ false);
|
||||
assertNotNull(blurRegionsForSfForNextFrame);
|
||||
assertEquals(1, blurRegionsForSfForNextFrame.length);
|
||||
// Assert second frame updates are applied normally
|
||||
assertEquals((float) TEST_BLUR_RADIUS, blurRegionsForSfForNextFrame[0][0]);
|
||||
assertEquals(5f, blurRegionsForSfForNextFrame[0][2]);
|
||||
assertEquals(6f, blurRegionsForSfForNextFrame[0][3]);
|
||||
assertEquals(7f, blurRegionsForSfForNextFrame[0][4]);
|
||||
assertEquals(8f, blurRegionsForSfForNextFrame[0][5]);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user