From 64e516f5fa56d71bf6b5076e5620c30dc5810ba1 Mon Sep 17 00:00:00 2001 From: Rob Carr Date: Thu, 29 Oct 2015 00:20:45 +0000 Subject: [PATCH] Sync SurfaceView position changes to parent render. In order to provide pixel perfect movement of SurfaceViews 'within' other views (e.g. scrolling) we need to be able to synchronize the attached (parent window) painting with the movement of the SurfaceView (recall, SurfaceViews are positioned behind their attached windows and the parent must render a transparent region for the SurfaceView to appear). Provide a new WindowManager method to reposition an attaching window (that is to say, a window which has an attached window like SurfaceView) and defer the transaction until the parent frame. SurfaceView is hooked up to use this for movement. This is still 'racy' in the hardware accelerated case as the render thread could be on either side of dequeing the frame we are working on. Bug: 22802885 Change-Id: I025d2bdcbe15c1c11047cc0dbca2cf2b7d67c632 --- core/java/android/view/IWindowSession.aidl | 17 +++++++ core/java/android/view/Surface.java | 14 ++++++ core/java/android/view/SurfaceControl.java | 11 +++++ core/java/android/view/SurfaceView.java | 14 +++++- core/java/android/view/ViewRootImpl.java | 14 ++++++ core/jni/android_view_Surface.cpp | 5 ++ core/jni/android_view_SurfaceControl.cpp | 19 ++++++++ .../java/com/android/server/wm/Session.java | 6 +++ .../server/wm/WindowManagerService.java | 48 +++++++++++++++++++ 9 files changed, 146 insertions(+), 2 deletions(-) diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 017364a5a0b12..1be2f95cf55a3 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -95,6 +95,23 @@ interface IWindowSession { out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets, out Rect outOutsets, out Configuration outConfig, out Surface outSurface); + /** + * Position a window relative to it's parent (attached) window without triggering + * a full relayout. This action may be deferred until a given frame number + * for the parent window appears. This allows for synchronizing movement of a child + * to repainting the contents of the parent. + * + * @param window The window being modified. Must be attached to a parent window + * or this call will fail. + * @param x The new x position + * @param y The new y position + * @param deferTransactionUntilFrame Frame number from our parent (attached) to + * defer this action until. + * @param outFrame Rect in which is placed the new position/size on screen. + */ + void repositionChild(IWindow childWindow, int x, int y, long deferTransactionUntilFrame, + out Rect outFrame); + /** * If a call to relayout() asked to have the surface destroy deferred, * it must call this once it is okay to destroy that surface. diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 6de4d3e4e4c3d..394660fb538b1 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -56,6 +56,8 @@ public class Surface implements Parcelable { private static native int nativeGetWidth(long nativeObject); private static native int nativeGetHeight(long nativeObject); + private static native long nativeGetNextFrameNumber(long nativeObject); + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override @@ -219,6 +221,18 @@ public class Surface implements Parcelable { } } + /** + * Returns the next frame number which will be dequeued for rendering. + * Intended for use with SurfaceFlinger's deferred transactions API. + * + * @hide + */ + public long getNextFrameNumber() { + synchronized (mLock) { + return nativeGetNextFrameNumber(mNativeObject); + } + } + /** * Returns true if the consumer of this Surface is running behind the producer. * diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index bcf9b2c7fe7c0..b58c68f782eeb 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -81,6 +81,9 @@ public class SurfaceControl { private static native boolean nativeSetActiveConfig(IBinder displayToken, int id); private static native void nativeSetDisplayPowerMode( IBinder displayToken, int mode); + private static native void nativeDeferTransactionUntil(long nativeObject, + IBinder handle, long frame); + private static native IBinder nativeGetHandle(long nativeObject); private final CloseGuard mCloseGuard = CloseGuard.get(); @@ -358,6 +361,14 @@ public class SurfaceControl { nativeCloseTransaction(); } + public void deferTransactionUntil(IBinder handle, long frame) { + nativeDeferTransactionUntil(mNativeObject, handle, frame); + } + + public IBinder getHandle() { + return nativeGetHandle(mNativeObject); + } + /** flag the transaction as an animation */ public static void setAnimationTransaction() { nativeSetAnimationTransaction(); diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index db68c29d025ff..dddea210ed834 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -448,11 +448,10 @@ public class SurfaceView extends View { final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight; final boolean visibleChanged = mVisible != mRequestedVisible; final boolean layoutSizeChanged = getWidth() != mLayout.width || getHeight() != mLayout.height; + final boolean positionChanged = mLeft != mLocation[0] || mTop != mLocation[1]; if (force || creating || formatChanged || sizeChanged || visibleChanged - || mLeft != mLocation[0] || mTop != mLocation[1] || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded || layoutSizeChanged) { - if (DEBUG) Log.i(TAG, "Changes: creating=" + creating + " format=" + formatChanged + " size=" + sizeChanged + " visible=" + visibleChanged @@ -616,11 +615,22 @@ public class SurfaceView extends View { mSession.performDeferredDestroy(mWindow); } } catch (RemoteException ex) { + Log.e(TAG, "Exception from relayout", ex); } if (DEBUG) Log.v( TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y + " w=" + mLayout.width + " h=" + mLayout.height + ", frame=" + mSurfaceFrame); + } else if (positionChanged) { // Only the position has changed + mLeft = mLocation[0]; + mTop = mLocation[1]; + try { + mSession.repositionChild(mWindow, mLeft, mTop, + viewRoot != null ? viewRoot.getNextFrameNumber() : -1, + mWinFrame); + } catch (RemoteException ex) { + Log.e(TAG, "Exception from relayout", ex); + } } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index e17bdd7d9a3e4..faeb353131749 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -6607,6 +6607,20 @@ public final class ViewRootImpl implements ViewParent, } } + long getNextFrameNumber() { + long frameNumber = -1; + if (mSurfaceHolder != null) { + mSurfaceHolder.mSurfaceLock.lock(); + } + if (mSurface.isValid()) { + frameNumber = mSurface.getNextFrameNumber(); + } + if (mSurfaceHolder != null) { + mSurfaceHolder.mSurfaceLock.unlock(); + } + return frameNumber; + } + class TakenSurfaceHolder extends BaseSurfaceHolder { @Override public boolean onAllowLockCanvas() { diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index da96b935883eb..ff51e4ee12638 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -460,6 +460,10 @@ static jint nativeGetHeight(JNIEnv* env, jclass clazz, jlong nativeObject) { anw->query(anw, NATIVE_WINDOW_HEIGHT, &value); return value; } +static jlong nativeGetNextFrameNumber(JNIEnv *env, jclass clazz, jlong nativeObject) { + Surface* surface = reinterpret_cast(nativeObject); + return surface->getNextFrameNumber(); +} namespace uirenderer { @@ -536,6 +540,7 @@ static const JNINativeMethod gSurfaceMethods[] = { (void*)nativeWriteToParcel }, {"nativeGetWidth", "(J)I", (void*)nativeGetWidth }, {"nativeGetHeight", "(J)I", (void*)nativeGetHeight }, + {"nativeGetNextFrameNumber", "(J)J", (void*)nativeGetNextFrameNumber }, // HWUI context {"nHwuiCreate", "(JJ)J", (void*) hwui::create }, diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 931ad54fb5e8d..1dfe40a324b3a 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -571,6 +571,21 @@ static jboolean nativeGetAnimationFrameStats(JNIEnv* env, jclass clazz, jobject return JNI_TRUE; } + +static void nativeDeferTransactionUntil(JNIEnv* env, jclass clazz, jlong nativeObject, + jobject handleObject, jlong frameNumber) { + auto ctrl = reinterpret_cast(nativeObject); + sp handle = ibinderForJavaObject(env, handleObject); + + ctrl->deferTransactionUntil(handle, frameNumber); +} + +static jobject nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) { + auto ctrl = reinterpret_cast(nativeObject); + + return javaObjectForIBinder(env, ctrl->getHandle()); +} + // ---------------------------------------------------------------------------- static const JNINativeMethod sSurfaceControlMethods[] = { @@ -638,6 +653,10 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeGetAnimationFrameStats }, {"nativeSetDisplayPowerMode", "(Landroid/os/IBinder;I)V", (void*)nativeSetDisplayPowerMode }, + {"nativeDeferTransactionUntil", "(JLandroid/os/IBinder;J)V", + (void*)nativeDeferTransactionUntil }, + {"nativeGetHandle", "(J)Landroid/os/IBinder;", + (void*)nativeGetHandle } }; int register_android_view_SurfaceControl(JNIEnv* env) diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 1f62bc15024e0..c47c377f4cb84 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -187,6 +187,12 @@ final class Session extends IWindowSession.Stub mService.removeWindow(this, window); } + @Override + public void repositionChild(IWindow window, int x, int y, long deferTransactionUntilFrame, + Rect outFrame) { + mService.repositionChild(this, window, x, y, deferTransactionUntilFrame, outFrame); + } + public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewFlags, int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index f31f28547c2a8..4ccaa30b7bec9 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2468,6 +2468,54 @@ public class WindowManagerService extends IWindowManager.Stub } } + void repositionChild(Session session, IWindow client, + int x, int y, long deferTransactionUntilFrame, Rect outFrame) { + Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "repositionChild"); + long origId = Binder.clearCallingIdentity(); + + try { + synchronized(mWindowMap) { + WindowState win = windowForClientLocked(session, client, false); + if (win == null) { + return; + } + if (win.mAttachedWindow == null) { + throw new IllegalArgumentException( + "repositionChild called but window is not" + + "attached to a parent win=" + win); + } + + win.mFrame.left = x; + win.mFrame.top = y; + + win.mWinAnimator.computeShownFrameLocked(); + + if (SHOW_TRANSACTIONS) { + Slog.i(TAG, ">>> OPEN TRANSACTION repositionChild"); + } + + SurfaceControl.openTransaction(); + + if (deferTransactionUntilFrame > 0) { + win.mWinAnimator.mSurfaceControl.deferTransactionUntil( + win.mAttachedWindow.mWinAnimator.mSurfaceControl.getHandle(), + deferTransactionUntilFrame); + } + win.mWinAnimator.setSurfaceBoundariesLocked(false); + + SurfaceControl.closeTransaction(); + if (SHOW_TRANSACTIONS) { + Slog.i(TAG, "<<< CLOSE TRANSACTION repositionChild"); + } + + outFrame = win.mCompatFrame; + } + } finally { + Binder.restoreCallingIdentity(origId); + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); + } + } + public int relayoutWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewVisibility, int flags,