Create cross window blur drawable

This introduces BackgroundBlurDrawable that does cross-window blurs
The drawable should not be created manually, it should be retrieved
from the ViewRootImpl

Test: manual
Bug: 159712515
Bug: 171916625
Change-Id: I697e93fc95ba3d0a7211b235b1c5d48a1968b939
This commit is contained in:
Lucas Dupin
2020-10-01 12:13:09 -07:00
parent 38af94c821
commit 690a360fad
6 changed files with 414 additions and 1 deletions

View File

@@ -135,6 +135,8 @@ public final class SurfaceControl implements Parcelable {
int blurRadius);
private static native void nativeSetLayerStack(long transactionObj, long nativeObject,
int layerStack);
private static native void nativeSetBlurRegions(long transactionObj, long nativeObj,
float[][] regions, int length);
private static native boolean nativeClearContentFrameStats(long nativeObject);
private static native boolean nativeGetContentFrameStats(long nativeObject, WindowContentFrameStats outStats);
@@ -2953,6 +2955,21 @@ public final class SurfaceControl implements Parcelable {
return this;
}
/**
* Specify what regions should be blurred on the {@link SurfaceControl}.
*
* @param sc SurfaceControl.
* @param regions List of regions that will have blurs.
* @return itself.
* @see BlurRegion#toFloatArray()
* @hide
*/
public Transaction setBlurRegions(SurfaceControl sc, float[][] regions) {
checkPreconditions(sc);
nativeSetBlurRegions(mNativeObject, sc.mNativeObject, regions, regions.length);
return this;
}
/**
* @hide
*/
@@ -3482,4 +3499,64 @@ 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
+ "}";
}
}
}

View File

@@ -187,6 +187,7 @@ import android.window.ClientWindowFrames;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.SomeArgs;
@@ -654,6 +655,9 @@ public final class ViewRootImpl implements ViewParent,
private ScrollCaptureConnection mScrollCaptureConnection;
private final BackgroundBlurDrawable.Aggregator mBlurRegionAggregator =
new BackgroundBlurDrawable.Aggregator(this);
/**
* @return {@link ImeFocusController} for this instance.
*/
@@ -3819,6 +3823,8 @@ public final class ViewRootImpl implements ViewParent,
private HardwareRenderer.FrameCompleteCallback createFrameCompleteCallback(Handler handler,
boolean reportNextDraw, ArrayList<Runnable> commitCallbacks) {
return frameNr -> {
mBlurRegionAggregator.dispatchBlurTransactionIfNeeded(frameNr);
// Use a new transaction here since mRtBLASTSyncTransaction can only be accessed by
// the render thread and mSurfaceChangedTransaction can only be accessed by the UI
// thread. The temporary transaction is used so mRtBLASTSyncTransaction can be merged
@@ -3849,7 +3855,8 @@ public final class ViewRootImpl implements ViewParent,
.captureFrameCommitCallbacks();
final boolean needFrameCompleteCallback =
mNextDrawUseBLASTSyncTransaction || mReportNextDraw
|| (commitCallbacks != null && commitCallbacks.size() > 0);
|| (commitCallbacks != null && commitCallbacks.size() > 0)
|| mBlurRegionAggregator.hasRegions();
if (needFrameCompleteCallback) {
mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(
createFrameCompleteCallback(mAttachInfo.mHandler, mReportNextDraw,
@@ -9915,6 +9922,36 @@ public final class ViewRootImpl implements ViewParent,
}
}
/**
* Sends a list of blur regions to SurfaceFlinger, tagged with a frame.
*
* @param regionCopy List of regions
* @param frameNumber Frame where it should be applied (or current when using BLAST)
*/
public void dispatchBlurRegions(float[][] regionCopy, long frameNumber) {
final SurfaceControl surfaceControl = getSurfaceControl();
if (!surfaceControl.isValid()) {
return;
}
if (useBLAST()) {
synchronized (getBlastTransactionLock()) {
getBLASTSyncTransaction().setBlurRegions(surfaceControl, regionCopy);
}
} else {
SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
transaction.setBlurRegions(surfaceControl, regionCopy);
transaction.deferTransactionUntil(surfaceControl, getSurfaceControl(), frameNumber);
transaction.apply();
}
}
/**
* Creates a background blur drawable for the backing {@link Surface}.
*/
public BackgroundBlurDrawable createBackgroundBlurDrawable() {
return mBlurRegionAggregator.createBackgroundBlurDrawable(mContext);
}
SurfaceControl.Transaction getBLASTSyncTransaction() {
return mRtBLASTSyncTransaction;
}

View File

@@ -0,0 +1,257 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.graphics.drawable;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RenderNode;
import android.graphics.drawable.Drawable;
import android.util.ArrayMap;
import android.util.Log;
import android.view.SurfaceControl;
import android.view.ViewRootImpl;
import com.android.internal.R;
/**
* A drawable that keeps track of a blur region, pokes a hole under it, and propagates its state
* to SurfaceFlinger.
*/
public final class BackgroundBlurDrawable extends Drawable {
private static final String TAG = BackgroundBlurDrawable.class.getSimpleName();
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final Aggregator mAggregator;
private final RenderNode mRenderNode;
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 =
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);
}
}
@Override
public void positionLost(long frameNumber) {
synchronized (mAggregator) {
mBlurRegion.rect.setEmpty();
mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion);
}
}
};
private BackgroundBlurDrawable(Aggregator aggregator) {
mAggregator = aggregator;
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
mRenderNode = new RenderNode("BackgroundBlurDrawable");
mRenderNode.addPositionUpdateListener(mPositionUpdateListener);
}
@Override
public void draw(@NonNull Canvas canvas) {
if (mRectPath.isEmpty() || !isVisible() || getAlpha() == 0) {
return;
}
canvas.drawPath(mRectPath, mPaint);
canvas.drawRenderNode(mRenderNode);
}
@Override
public boolean setVisible(boolean visible, boolean restart) {
boolean changed = super.setVisible(visible, restart);
if (changed) {
mBlurRegion.visible = visible;
}
return changed;
}
@Override
public void setAlpha(int alpha) {
mBlurRegion.alpha = alpha / 255f;
invalidateSelf();
}
/**
* Blur radius in pixels.
*/
public void setBlurRadius(int blurRadius) {
mBlurRegion.blurRadius = blurRadius;
invalidateSelf();
}
/**
* Sets the corner radius, in degrees.
*/
public void setCornerRadius(float cornerRadius) {
setCornerRadius(cornerRadius, cornerRadius, cornerRadius, cornerRadius);
}
/**
* Sets the corner radius in degrees.
* @param cornerRadiusTL top left radius.
* @param cornerRadiusTR top right radius.
* @param cornerRadiusBL bottom left radius.
* @param cornerRadiusBR bottom right radius.
*/
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;
}
updatePath();
invalidateSelf();
}
@Override
public void setBounds(int left, int top, int right, int bottom) {
super.setBounds(left, top, right, bottom);
mRenderNode.setPosition(left, top, right, bottom);
updatePath();
}
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;
}
mRectPath.reset();
if (getAlpha() == 0 || !isVisible()) {
return;
}
Rect bounds = getBounds();
mRectPath.addRoundRect(bounds.left, bounds.top, bounds.right, bounds.bottom, mTmpRadii,
Path.Direction.CW);
}
@Override
public void setColorFilter(@Nullable ColorFilter colorFilter) {
throw new IllegalArgumentException("not implemented");
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
/**
* 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 ViewRootImpl mViewRoot;
private float[][] mTmpBlurRegionsArray;
private boolean mNeedsUpdate;
public Aggregator(ViewRootImpl viewRoot) {
mViewRoot = viewRoot;
}
/**
* Creates a blur region with default radius.
*/
public BackgroundBlurDrawable createBackgroundBlurDrawable(Context context) {
BackgroundBlurDrawable drawable = new BackgroundBlurDrawable(this);
drawable.setBlurRadius(context.getResources().getDimensionPixelSize(
R.dimen.default_background_blur_radius));
return drawable;
}
/**
* Called from RenderThread only, already locked.
* @param drawable
* @param blurRegion
*/
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);
}
} else {
mBlurRegions.put(drawable, blurRegion);
mNeedsUpdate = true;
if (DEBUG) {
Log.d(TAG, "Update " + blurRegion);
}
}
}
/**
* If there are any blur regions visible on the screen at the moment.
*/
public boolean hasRegions() {
return mBlurRegions.size() > 0;
}
/**
* Dispatch blur updates, if there were any.
* @param frameNumber Frame where the update should happen.
*/
public void dispatchBlurTransactionIfNeeded(long frameNumber) {
synchronized (this) {
if (!mNeedsUpdate) {
return;
}
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);
}
}
}
}

View File

@@ -40,6 +40,7 @@
#include <private/gui/ComposerService.h>
#include <stdio.h>
#include <system/graphics.h>
#include <ui/BlurRegion.h>
#include <ui/ConfigStoreTypes.h>
#include <ui/DeviceProductInfo.h>
#include <ui/DisplayConfig.h>
@@ -522,6 +523,43 @@ static void nativeSetGeometry(JNIEnv* env, jclass clazz, jlong transactionObj, j
transaction->setGeometry(ctrl, source, dst, orientation);
}
static void nativeSetBlurRegions(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jobjectArray regions, jint regionsLength) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
std::vector<BlurRegion> blurRegionVector;
const int size = regionsLength;
float region[10];
for (int i = 0; i < size; i++) {
jfloatArray regionArray = (jfloatArray)env->GetObjectArrayElement(regions, i);
env->GetFloatArrayRegion(regionArray, 0, 10, region);
float blurRadius = region[0];
float alpha = region[1];
float left = region[2];
float top = region[3];
float right = region[4];
float bottom = region[5];
float cornerRadiusTL = region[6];
float cornerRadiusTR = region[7];
float cornerRadiusBL = region[8];
float cornerRadiusBR = region[9];
blurRegionVector.push_back(BlurRegion{.blurRadius = static_cast<uint32_t>(blurRadius),
.cornerRadiusTL = cornerRadiusTL,
.cornerRadiusTR = cornerRadiusTR,
.cornerRadiusBL = cornerRadiusBL,
.cornerRadiusBR = cornerRadiusBR,
.alpha = alpha,
.left = static_cast<int>(left),
.top = static_cast<int>(top),
.right = static_cast<int>(right),
.bottom = static_cast<int>(bottom)});
}
transaction->setBlurRegions(ctrl, blurRegionVector);
}
static void nativeSetSize(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jint w, jint h) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -1626,6 +1664,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeSetBackgroundBlurRadius },
{"nativeSetLayerStack", "(JJI)V",
(void*)nativeSetLayerStack },
{"nativeSetBlurRegions", "(JJ[[FI)V",
(void*)nativeSetBlurRegions },
{"nativeSetShadowRadius", "(JJF)V",
(void*)nativeSetShadowRadius },
{"nativeSetFrameRate", "(JJFI)V",

View File

@@ -858,6 +858,7 @@
<dimen name="waterfall_display_right_edge_size">0px</dimen>
<dimen name="waterfall_display_bottom_edge_size">0px</dimen>
<dimen name="default_background_blur_radius">100dp</dimen>
<!-- The maximum height of a thumbnail in a ThumbnailTemplate. The image will be reduced to that height in case they are bigger. -->
<dimen name="controls_thumbnail_image_max_height">140dp</dimen>
<!-- The maximum width of a thumbnail in a ThumbnailTemplate. The image will be reduced to that width in case they are bigger.-->

View File

@@ -4078,6 +4078,7 @@
<java-symbol type="integer" name="config_defaultBinderHeavyHitterAutoSamplerBatchSize" />
<java-symbol type="dimen" name="config_defaultBinderHeavyHitterAutoSamplerThreshold" />
<java-symbol type="dimen" name="default_background_blur_radius" />
<java-symbol type="array" name="config_keep_warming_services" />
<java-symbol type="string" name="config_display_features" />
<java-symbol type="array" name="config_internalFoldedPhysicalDisplayIds" />