summaryrefslogtreecommitdiff
path: root/core/java/android/view/SurfaceView.java
diff options
context:
space:
mode:
authorVishnu Nair <vishnun@google.com>2020-09-16 16:00:39 -0700
committerVishnu Nair <vishnun@google.com>2020-09-24 21:45:18 +0000
commitcf143d44088368e383fe103a2580928780ccee19 (patch)
tree6b6ae70c42f8c042994e0b7ce865aa799ec099df /core/java/android/view/SurfaceView.java
parentf502b885b9fd17f1e272c101833af1489a91dc59 (diff)
Add BlastBufferQueue adapter in SurfaceView
Add support for submitting buffers in SurfaceView via BLAST using BlastBufferQueue adapter. Introduce a new config wm_use_blast_adapter_sv, that is disabled by default to enable the adapter. When enabled, the blast SC is created as a child of the main SurfaceView SC and the main SC is set as a container layer. This layer will continue to handle position, visibility and transforms while the blast SC will handle buffer updates via the adapter. Test: atest SurfaceViewBufferTests Test: go/wm-smoke w/ & w/o adb shell adb shell settings set global use_blast_adapter_sv 1 Bug: b/168504870, b/168917217 Change-Id: I826eef39e03ea339df54400be0709eaba6c88797
Diffstat (limited to 'core/java/android/view/SurfaceView.java')
-rw-r--r--core/java/android/view/SurfaceView.java252
1 files changed, 173 insertions, 79 deletions
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index abda698fdf73..a8ec9edf398d 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -23,8 +23,10 @@ import static android.view.WindowManagerPolicyConstants.APPLICATION_PANEL_SUBLAY
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.res.CompatibilityInfo.Translator;
+import android.graphics.BLASTBufferQueue;
import android.graphics.BlendMode;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -41,6 +43,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceControl.Transaction;
@@ -232,6 +235,19 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
SurfaceControlViewHost.SurfacePackage mSurfacePackage;
+ /**
+ * Returns {@code true} if buffers should be submitted via blast
+ */
+ private static boolean useBlastAdapter(Context context) {
+ ContentResolver contentResolver = context.getContentResolver();
+ return Settings.Global.getInt(contentResolver,
+ Settings.Global.DEVELOPMENT_USE_BLAST_ADAPTER_SV, 0 /* default */) == 1;
+ }
+
+ private final boolean mUseBlastAdapter;
+ private SurfaceControl mBlastSurfaceControl;
+ private BLASTBufferQueue mBlastBufferQueue;
+
public SurfaceView(Context context) {
this(context, null);
}
@@ -252,6 +268,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
public SurfaceView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
int defStyleRes, boolean disableBackgroundLayer) {
super(context, attrs, defStyleAttr, defStyleRes);
+ mUseBlastAdapter = useBlastAdapter(context);
mRenderNode.addPositionUpdateListener(mPositionListener);
setWillNotDraw(true);
@@ -531,7 +548,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
mRequestedVisible = false;
updateSurface();
- releaseSurfaces();
+ tryReleaseSurfaces();
// We don't release this as part of releaseSurfaces as
// that is also called on transient visibility changes. We can't
@@ -875,7 +892,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
return t;
}
- private void releaseSurfaces() {
+ private void tryReleaseSurfaces() {
mSurfaceAlpha = 1f;
synchronized (mSurfaceControlLock) {
@@ -886,18 +903,30 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
return;
}
- if (mSurfaceControl != null) {
- mTmpTransaction.remove(mSurfaceControl);
- mSurfaceControl = null;
- }
- if (mBackgroundControl != null) {
- mTmpTransaction.remove(mBackgroundControl);
- mBackgroundControl = null;
- }
+ releaseSurfaces(mTmpTransaction);
mTmpTransaction.apply();
}
}
+ private void releaseSurfaces(Transaction transaction) {
+ if (mSurfaceControl != null) {
+ transaction.remove(mSurfaceControl);
+ mSurfaceControl = null;
+ }
+ if (mBackgroundControl != null) {
+ transaction.remove(mBackgroundControl);
+ mBackgroundControl = null;
+ }
+ if (mBlastSurfaceControl != null) {
+ transaction.remove(mBlastSurfaceControl);
+ mBlastSurfaceControl = null;
+ }
+ if (mBlastBufferQueue != null) {
+ mBlastBufferQueue.destroy();
+ mBlastBufferQueue = null;
+ }
+ }
+
/** @hide */
protected void updateSurface() {
if (!mHaveFrame) {
@@ -914,7 +943,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
if (viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) {
notifySurfaceDestroyed();
- releaseSurfaces();
+ tryReleaseSurfaces();
return;
}
@@ -979,45 +1008,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
mScreenRect.offset(surfaceInsets.left, surfaceInsets.top);
if (creating) {
- mDeferredDestroySurfaceControl = mSurfaceControl;
-
updateOpaqueFlag();
- // SurfaceView hierarchy
- // ViewRootImpl surface
- // - bounds layer (crops all child surfaces to parent surface insets)
- // - SurfaceView surface (drawn relative to ViewRootImpl surface)
- // - Background color layer (drawn behind all SurfaceView surfaces)
- //
- // The bounds layer is used to crop the surface view so it does not draw into
- // the parent surface inset region. Since there can be multiple surface views
- // below or above the parent surface, one option is to create multiple bounds
- // layer for each z order. The other option, the one implement is to create
- // a single bounds layer and set z order for each child surface relative to the
- // parent surface.
- // When creating the surface view, we parent it to the bounds layer and then
- // set the relative z order. When the parent surface changes, we have to
- // make sure to update the relative z via ViewRootImpl.SurfaceChangedCallback.
- final String name = "SurfaceView - " + viewRoot.getTitle().toString();
-
- mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
- .setName(name)
- .setLocalOwnerView(this)
- .setOpaque((mSurfaceFlags & SurfaceControl.OPAQUE) != 0)
- .setBufferSize(mSurfaceWidth, mSurfaceHeight)
- .setFormat(mFormat)
- .setParent(viewRoot.getBoundsLayer())
- .setFlags(mSurfaceFlags)
- .setCallsite("SurfaceView.updateSurface")
- .build();
- mBackgroundControl = new SurfaceControl.Builder(mSurfaceSession)
- .setName("Background for -" + name)
- .setLocalOwnerView(this)
- .setOpaque(true)
- .setColorLayer()
- .setParent(mSurfaceControl)
- .setCallsite("SurfaceView.updateSurface")
- .build();
-
+ mDeferredDestroySurfaceControl = createSurfaceControls(viewRoot);
} else if (mSurfaceControl == null) {
return;
}
@@ -1090,8 +1082,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
mTmpTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
if (sizeChanged && !creating) {
- mTmpTransaction.setBufferSize(mSurfaceControl, mSurfaceWidth,
- mSurfaceHeight);
+ setBufferSize(mTmpTransaction);
}
mTmpTransaction.apply();
@@ -1133,19 +1124,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
notifySurfaceDestroyed();
}
- if (creating) {
- mSurface.copyFrom(mSurfaceControl);
- }
-
- if (sizeChanged && getContext().getApplicationInfo().targetSdkVersion
- < Build.VERSION_CODES.O) {
- // Some legacy applications use the underlying native {@link Surface} object
- // as a key to whether anything has changed. In these cases, updates to the
- // existing {@link Surface} will be ignored when the size changes.
- // Therefore, we must explicitly recreate the {@link Surface} in these
- // cases.
- mSurface.createFrom(mSurfaceControl);
- }
+ copySurface(creating /* surfaceControlCreated */, sizeChanged);
if (visible && mSurface.isValid()) {
if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
@@ -1189,7 +1168,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
} finally {
mIsCreating = false;
if (mSurfaceControl != null && !mSurfaceCreated) {
- releaseSurfaces();
+ tryReleaseSurfaces();
}
}
} catch (Exception ex) {
@@ -1202,6 +1181,119 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
}
+ /**
+ * Copy the Surface from the SurfaceControl or the blast adapter.
+ *
+ * @param surfaceControlCreated true if we created the SurfaceControl and need to update our
+ * Surface if needed.
+ * @param bufferSizeChanged true if the BufferSize has changed and we need to recreate the
+ * Surface for compatibility reasons.
+ */
+ private void copySurface(boolean surfaceControlCreated, boolean bufferSizeChanged) {
+ if (surfaceControlCreated) {
+ if (mUseBlastAdapter) {
+ mSurface.copyFrom(mBlastBufferQueue);
+ } else {
+ mSurface.copyFrom(mSurfaceControl);
+ }
+ }
+
+ if (bufferSizeChanged && getContext().getApplicationInfo().targetSdkVersion
+ < Build.VERSION_CODES.O) {
+ // Some legacy applications use the underlying native {@link Surface} object
+ // as a key to whether anything has changed. In these cases, updates to the
+ // existing {@link Surface} will be ignored when the size changes.
+ // Therefore, we must explicitly recreate the {@link Surface} in these
+ // cases.
+ if (mUseBlastAdapter) {
+ mSurface.transferFrom(mBlastBufferQueue.createSurface());
+ } else {
+ mSurface.createFrom(mSurfaceControl);
+ }
+ }
+ }
+
+ private void setBufferSize(Transaction transaction) {
+ if (mUseBlastAdapter) {
+ mBlastBufferQueue.update(mBlastSurfaceControl, mSurfaceWidth,
+ mSurfaceHeight);
+ } else {
+ transaction.setBufferSize(mSurfaceControl, mSurfaceWidth,
+ mSurfaceHeight);
+ }
+ }
+
+ /**
+ * Creates the surface control hierarchy as follows
+ * ViewRootImpl surface
+ * bounds layer (crops all child surfaces to parent surface insets)
+ * * SurfaceView surface (drawn relative to ViewRootImpl surface)
+ * * Blast surface (if enabled)
+ * * Background color layer (drawn behind all SurfaceView surfaces)
+ *
+ * The bounds layer is used to crop the surface view so it does not draw into the parent
+ * surface inset region. Since there can be multiple surface views below or above the parent
+ * surface, one option is to create multiple bounds layer for each z order. The other option,
+ * the one implement is to create a single bounds layer and set z order for each child surface
+ * relative to the parent surface.
+ * When creating the surface view, we parent it to the bounds layer and then set the relative z
+ * order. When the parent surface changes, we have to make sure to update the relative z via
+ * ViewRootImpl.SurfaceChangedCallback.
+ *
+ * @return previous SurfaceControl where the content was rendered. In the surface is switched
+ * out, the old surface can be persevered until the new one has drawn by keeping the reference
+ * of the old SurfaceControl alive.
+ */
+ private SurfaceControl createSurfaceControls(ViewRootImpl viewRoot) {
+ final String name = "SurfaceView - " + viewRoot.getTitle().toString();
+
+ SurfaceControl.Builder builder = new SurfaceControl.Builder(mSurfaceSession)
+ .setName(name)
+ .setLocalOwnerView(this)
+ .setParent(viewRoot.getBoundsLayer())
+ .setCallsite("SurfaceView.updateSurface");
+
+ final SurfaceControl previousSurfaceControl;
+ if (mUseBlastAdapter) {
+ mSurfaceControl = builder
+ .setContainerLayer()
+ .build();
+ previousSurfaceControl = mBlastSurfaceControl;
+ mBlastSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
+ .setName(name + "(BLAST)")
+ .setLocalOwnerView(this)
+ .setBufferSize(mSurfaceWidth, mSurfaceHeight)
+ .setFormat(mFormat)
+ .setParent(mSurfaceControl)
+ .setFlags(mSurfaceFlags)
+ .setHidden(false)
+ .setBLASTLayer()
+ .setCallsite("SurfaceView.updateSurface")
+ .build();
+ mBlastBufferQueue = new BLASTBufferQueue(
+ mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight, true /* TODO */);
+ } else {
+ previousSurfaceControl = mSurfaceControl;
+ mSurfaceControl = builder
+ .setBufferSize(mSurfaceWidth, mSurfaceHeight)
+ .setFlags(mSurfaceFlags)
+ .setFormat(mFormat)
+ .build();
+ mBlastSurfaceControl = null;
+ mBlastBufferQueue = null;
+ }
+ mBackgroundControl = new SurfaceControl.Builder(mSurfaceSession)
+ .setName("Background for " + name)
+ .setLocalOwnerView(this)
+ .setOpaque(true)
+ .setColorLayer()
+ .setParent(mSurfaceControl)
+ .setCallsite("SurfaceView.updateSurface")
+ .build();
+
+ return previousSurfaceControl;
+ }
+
private void onDrawFinished() {
if (DEBUG) {
Log.i(TAG, System.identityHashCode(this) + " "
@@ -1348,16 +1440,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
}
- private void releaseSurfaces(Transaction t) {
- if (mRtReleaseSurfaces) {
- mRtReleaseSurfaces = false;
- t.remove(mSurfaceControl);
- t.remove(mBackgroundControl);
- mSurfaceControl = null;
- mBackgroundControl = null;
- }
- }
-
@Override
public void positionLost(long frameNumber) {
final ViewRootImpl viewRoot = getViewRootImpl();
@@ -1380,7 +1462,10 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
if (useBLAST) {
synchronized (viewRoot.getBlastTransactionLock()) {
viewRoot.getBLASTSyncTransaction().hide(mSurfaceControl);
- releaseSurfaces(viewRoot.getBLASTSyncTransaction());
+ if (mRtReleaseSurfaces) {
+ mRtReleaseSurfaces = false;
+ releaseSurfaces(viewRoot.getBLASTSyncTransaction());
+ }
}
} else {
if (frameNumber > 0 && viewRoot != null && viewRoot.mSurface.isValid()) {
@@ -1388,7 +1473,10 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
viewRoot.getRenderSurfaceControl(), frameNumber);
}
mRtTransaction.hide(mSurfaceControl);
- releaseSurfaces(mRtTransaction);
+ if (mRtReleaseSurfaces) {
+ mRtReleaseSurfaces = false;
+ releaseSurfaces(mRtTransaction);
+ }
// If we aren't using BLAST, we apply the transaction locally, otherwise we let
// the ViewRoot apply it for us.
// If the ViewRoot is null, we behave as if we aren't using BLAST so we need to
@@ -1716,7 +1804,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
* @param p The SurfacePackage to embed.
*/
public void setChildSurfacePackage(@NonNull SurfaceControlViewHost.SurfacePackage p) {
- final SurfaceControl sc = p != null ? p.getSurfaceControl() : null;
final SurfaceControl lastSc = mSurfacePackage != null ?
mSurfacePackage.getSurfaceControl() : null;
if (mSurfaceControl != null && lastSc != null) {
@@ -1733,7 +1820,14 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
SurfaceControlViewHost.SurfacePackage p) {
initEmbeddedHierarchyForAccessibility(p);
final SurfaceControl sc = p.getSurfaceControl();
- t.reparent(sc, mSurfaceControl).show(sc);
+ final SurfaceControl parent;
+ if (mUseBlastAdapter) {
+ parent = mBlastSurfaceControl;
+ } else {
+ parent = mSurfaceControl;
+ }
+
+ t.reparent(sc, parent).show(sc);
}
/** @hide */