Loading services/core/java/com/android/server/wm/SurfaceAnimationRunner.java +75 −35 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.wm; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.util.TimeUtils.NANOS_PER_MS; import static android.view.Choreographer.CALLBACK_TRAVERSAL; import static android.view.Choreographer.getSfInstance; Loading @@ -26,14 +27,13 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.Nullable; import android.graphics.Canvas; import android.graphics.Insets; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Rect; import android.hardware.power.Boost; import android.os.Handler; import android.os.PowerManagerInternal; import android.os.Trace; import android.util.ArrayMap; import android.util.Log; import android.view.Choreographer; Loading @@ -50,6 +50,8 @@ import com.android.server.AnimationThread; import com.android.server.wm.LocalAnimationAdapter.AnimationSpec; import java.util.ArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.function.Supplier; /** Loading Loading @@ -83,6 +85,12 @@ class SurfaceAnimationRunner { private final PowerManagerInternal mPowerManagerInternal; private boolean mApplyScheduled; // Executor to perform the edge extension. // With two threads because in practice we will want to extend two surfaces in one animation, // in which case we want to be able to parallelize those two extensions to cut down latency in // starting the animation. private final ExecutorService mEdgeExtensionExecutor = Executors.newFixedThreadPool(2); @GuardedBy("mLock") @VisibleForTesting final ArrayMap<SurfaceControl, RunningAnimation> mPendingAnimations = new ArrayMap<>(); Loading Loading @@ -173,7 +181,7 @@ class SurfaceAnimationRunner { // We must wait for t to be committed since otherwise the leash doesn't have the // windows we want to screenshot and extend as children. t.addTransactionCommittedListener(Runnable::run, () -> { t.addTransactionCommittedListener(mEdgeExtensionExecutor, () -> { final WindowAnimationSpec animationSpec = a.asWindowAnimationSpec(); final Transaction edgeExtensionCreationTransaction = new Transaction(); Loading Loading @@ -403,30 +411,17 @@ class SurfaceAnimationRunner { private void createExtensionSurface(SurfaceControl leash, Rect edgeBounds, Rect extensionRect, int xPos, int yPos, String layerName, Transaction startTransaction) { synchronized (mEdgeExtensionLock) { if (!mEdgeExtensions.containsKey(leash)) { // Animation leash has already been removed so we shouldn't perform any extension return; } createExtensionSurfaceLocked(leash, edgeBounds, extensionRect, xPos, yPos, layerName, Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "createExtensionSurface"); doCreateExtensionSurface(leash, edgeBounds, extensionRect, xPos, yPos, layerName, startTransaction); } Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } private void createExtensionSurfaceLocked(SurfaceControl surfaceToExtend, Rect edgeBounds, private void doCreateExtensionSurface(SurfaceControl leash, Rect edgeBounds, Rect extensionRect, int xPos, int yPos, String layerName, Transaction startTransaction) { final SurfaceControl edgeExtensionLayer = new SurfaceControl.Builder() .setName(layerName) .setParent(surfaceToExtend) .setHidden(true) .setCallsite("DefaultTransitionHandler#startAnimation") .setOpaque(true) .setBufferSize(extensionRect.width(), extensionRect.height()) .build(); SurfaceControl.LayerCaptureArgs captureArgs = new SurfaceControl.LayerCaptureArgs.Builder(surfaceToExtend) new SurfaceControl.LayerCaptureArgs.Builder(leash /* surfaceToExtend */) .setSourceCrop(edgeBounds) .setFrameScale(1) .setPixelFormat(PixelFormat.RGBA_8888) Loading @@ -437,31 +432,76 @@ class SurfaceAnimationRunner { SurfaceControl.captureLayers(captureArgs); if (edgeBuffer == null) { // The leash we are trying to screenshot may have been removed by this point, which is // likely the reason for ending up with a null edgeBuffer, in which case we just want to // return and do nothing. Log.e("SurfaceAnimationRunner", "Failed to create edge extension - " + "edge buffer is null"); return; } android.graphics.BitmapShader shader = new android.graphics.BitmapShader(edgeBuffer.asBitmap(), android.graphics.Shader.TileMode.CLAMP, android.graphics.Shader.TileMode.CLAMP); final Paint paint = new Paint(); paint.setShader(shader); final SurfaceControl edgeExtensionLayer = new SurfaceControl.Builder() .setName(layerName) .setHidden(true) .setCallsite("DefaultTransitionHandler#startAnimation") .setOpaque(true) .setBufferSize(edgeBounds.width(), edgeBounds.height()) .build(); final Surface surface = new Surface(edgeExtensionLayer); Canvas c = surface.lockHardwareCanvas(); c.drawRect(extensionRect, paint); surface.unlockCanvasAndPost(c); surface.attachAndQueueBufferWithColorSpace(edgeBuffer.getHardwareBuffer(), edgeBuffer.getColorSpace()); surface.release(); final float scaleX = getScaleXForExtensionSurface(edgeBounds, extensionRect); final float scaleY = getScaleYForExtensionSurface(edgeBounds, extensionRect); synchronized (mEdgeExtensionLock) { if (!mEdgeExtensions.containsKey(leash)) { // The animation leash has already been removed, so we don't want to attach the // edgeExtension layer and should immediately remove it instead. startTransaction.remove(edgeExtensionLayer); return; } startTransaction.setScale(edgeExtensionLayer, scaleX, scaleY); startTransaction.reparent(edgeExtensionLayer, leash); startTransaction.setLayer(edgeExtensionLayer, Integer.MIN_VALUE); startTransaction.setPosition(edgeExtensionLayer, xPos, yPos); startTransaction.setVisibility(edgeExtensionLayer, true); mEdgeExtensions.get(surfaceToExtend).add(edgeExtensionLayer); mEdgeExtensions.get(leash).add(edgeExtensionLayer); } } private float getScaleXForExtensionSurface(Rect edgeBounds, Rect extensionRect) { if (edgeBounds.width() == extensionRect.width()) { // Top or bottom edge extension, no need to scale the X axis of the extension surface. return 1; } if (edgeBounds.width() == 1) { // Left or right edge extension, scale the surface to be the extensionRect's width. return extensionRect.width(); } throw new RuntimeException("Unexpected edgeBounds and extensionRect widths"); } private float getScaleYForExtensionSurface(Rect edgeBounds, Rect extensionRect) { if (edgeBounds.height() == extensionRect.height()) { // Left or right edge extension, no need to scale the Y axis of the extension surface. return 1; } if (edgeBounds.height() == 1) { // Top or bottom edge extension, scale the surface to be the extensionRect's height. return extensionRect.height(); } throw new RuntimeException("Unexpected edgeBounds and extensionRect heights"); } private static final class RunningAnimation { final AnimationSpec mAnimSpec; final SurfaceControl mLeash; Loading Loading
services/core/java/com/android/server/wm/SurfaceAnimationRunner.java +75 −35 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.wm; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.util.TimeUtils.NANOS_PER_MS; import static android.view.Choreographer.CALLBACK_TRAVERSAL; import static android.view.Choreographer.getSfInstance; Loading @@ -26,14 +27,13 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.Nullable; import android.graphics.Canvas; import android.graphics.Insets; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Rect; import android.hardware.power.Boost; import android.os.Handler; import android.os.PowerManagerInternal; import android.os.Trace; import android.util.ArrayMap; import android.util.Log; import android.view.Choreographer; Loading @@ -50,6 +50,8 @@ import com.android.server.AnimationThread; import com.android.server.wm.LocalAnimationAdapter.AnimationSpec; import java.util.ArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.function.Supplier; /** Loading Loading @@ -83,6 +85,12 @@ class SurfaceAnimationRunner { private final PowerManagerInternal mPowerManagerInternal; private boolean mApplyScheduled; // Executor to perform the edge extension. // With two threads because in practice we will want to extend two surfaces in one animation, // in which case we want to be able to parallelize those two extensions to cut down latency in // starting the animation. private final ExecutorService mEdgeExtensionExecutor = Executors.newFixedThreadPool(2); @GuardedBy("mLock") @VisibleForTesting final ArrayMap<SurfaceControl, RunningAnimation> mPendingAnimations = new ArrayMap<>(); Loading Loading @@ -173,7 +181,7 @@ class SurfaceAnimationRunner { // We must wait for t to be committed since otherwise the leash doesn't have the // windows we want to screenshot and extend as children. t.addTransactionCommittedListener(Runnable::run, () -> { t.addTransactionCommittedListener(mEdgeExtensionExecutor, () -> { final WindowAnimationSpec animationSpec = a.asWindowAnimationSpec(); final Transaction edgeExtensionCreationTransaction = new Transaction(); Loading Loading @@ -403,30 +411,17 @@ class SurfaceAnimationRunner { private void createExtensionSurface(SurfaceControl leash, Rect edgeBounds, Rect extensionRect, int xPos, int yPos, String layerName, Transaction startTransaction) { synchronized (mEdgeExtensionLock) { if (!mEdgeExtensions.containsKey(leash)) { // Animation leash has already been removed so we shouldn't perform any extension return; } createExtensionSurfaceLocked(leash, edgeBounds, extensionRect, xPos, yPos, layerName, Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "createExtensionSurface"); doCreateExtensionSurface(leash, edgeBounds, extensionRect, xPos, yPos, layerName, startTransaction); } Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } private void createExtensionSurfaceLocked(SurfaceControl surfaceToExtend, Rect edgeBounds, private void doCreateExtensionSurface(SurfaceControl leash, Rect edgeBounds, Rect extensionRect, int xPos, int yPos, String layerName, Transaction startTransaction) { final SurfaceControl edgeExtensionLayer = new SurfaceControl.Builder() .setName(layerName) .setParent(surfaceToExtend) .setHidden(true) .setCallsite("DefaultTransitionHandler#startAnimation") .setOpaque(true) .setBufferSize(extensionRect.width(), extensionRect.height()) .build(); SurfaceControl.LayerCaptureArgs captureArgs = new SurfaceControl.LayerCaptureArgs.Builder(surfaceToExtend) new SurfaceControl.LayerCaptureArgs.Builder(leash /* surfaceToExtend */) .setSourceCrop(edgeBounds) .setFrameScale(1) .setPixelFormat(PixelFormat.RGBA_8888) Loading @@ -437,31 +432,76 @@ class SurfaceAnimationRunner { SurfaceControl.captureLayers(captureArgs); if (edgeBuffer == null) { // The leash we are trying to screenshot may have been removed by this point, which is // likely the reason for ending up with a null edgeBuffer, in which case we just want to // return and do nothing. Log.e("SurfaceAnimationRunner", "Failed to create edge extension - " + "edge buffer is null"); return; } android.graphics.BitmapShader shader = new android.graphics.BitmapShader(edgeBuffer.asBitmap(), android.graphics.Shader.TileMode.CLAMP, android.graphics.Shader.TileMode.CLAMP); final Paint paint = new Paint(); paint.setShader(shader); final SurfaceControl edgeExtensionLayer = new SurfaceControl.Builder() .setName(layerName) .setHidden(true) .setCallsite("DefaultTransitionHandler#startAnimation") .setOpaque(true) .setBufferSize(edgeBounds.width(), edgeBounds.height()) .build(); final Surface surface = new Surface(edgeExtensionLayer); Canvas c = surface.lockHardwareCanvas(); c.drawRect(extensionRect, paint); surface.unlockCanvasAndPost(c); surface.attachAndQueueBufferWithColorSpace(edgeBuffer.getHardwareBuffer(), edgeBuffer.getColorSpace()); surface.release(); final float scaleX = getScaleXForExtensionSurface(edgeBounds, extensionRect); final float scaleY = getScaleYForExtensionSurface(edgeBounds, extensionRect); synchronized (mEdgeExtensionLock) { if (!mEdgeExtensions.containsKey(leash)) { // The animation leash has already been removed, so we don't want to attach the // edgeExtension layer and should immediately remove it instead. startTransaction.remove(edgeExtensionLayer); return; } startTransaction.setScale(edgeExtensionLayer, scaleX, scaleY); startTransaction.reparent(edgeExtensionLayer, leash); startTransaction.setLayer(edgeExtensionLayer, Integer.MIN_VALUE); startTransaction.setPosition(edgeExtensionLayer, xPos, yPos); startTransaction.setVisibility(edgeExtensionLayer, true); mEdgeExtensions.get(surfaceToExtend).add(edgeExtensionLayer); mEdgeExtensions.get(leash).add(edgeExtensionLayer); } } private float getScaleXForExtensionSurface(Rect edgeBounds, Rect extensionRect) { if (edgeBounds.width() == extensionRect.width()) { // Top or bottom edge extension, no need to scale the X axis of the extension surface. return 1; } if (edgeBounds.width() == 1) { // Left or right edge extension, scale the surface to be the extensionRect's width. return extensionRect.width(); } throw new RuntimeException("Unexpected edgeBounds and extensionRect widths"); } private float getScaleYForExtensionSurface(Rect edgeBounds, Rect extensionRect) { if (edgeBounds.height() == extensionRect.height()) { // Left or right edge extension, no need to scale the Y axis of the extension surface. return 1; } if (edgeBounds.height() == 1) { // Top or bottom edge extension, scale the surface to be the extensionRect's height. return extensionRect.height(); } throw new RuntimeException("Unexpected edgeBounds and extensionRect heights"); } private static final class RunningAnimation { final AnimationSpec mAnimSpec; final SurfaceControl mLeash; Loading