Properly run window animations at vsync-sf (1/2)
- Add new Choreographer instance that runs on vsync-sf - Use this new Choreographer for WindowAnimator, and remove all the hacks around it Test: Open apps and close apps, notice no stutter Test: Screen zoom animations Test: go/wm-smoke Bug: 36631902 Change-Id: I988ae25645effc3ac20efa7cb9b68f23444da0d0
This commit is contained in:
@@ -16,6 +16,9 @@
|
||||
|
||||
package android.view;
|
||||
|
||||
import static android.view.DisplayEventReceiver.VSYNC_SOURCE_APP;
|
||||
import static android.view.DisplayEventReceiver.VSYNC_SOURCE_SURFACE_FLINGER;
|
||||
|
||||
import android.hardware.display.DisplayManagerGlobal;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
@@ -102,10 +105,23 @@ public final class Choreographer {
|
||||
if (looper == null) {
|
||||
throw new IllegalStateException("The current thread must have a looper!");
|
||||
}
|
||||
return new Choreographer(looper);
|
||||
return new Choreographer(looper, VSYNC_SOURCE_APP);
|
||||
}
|
||||
};
|
||||
|
||||
// Thread local storage for the SF choreographer.
|
||||
private static final ThreadLocal<Choreographer> sSfThreadInstance =
|
||||
new ThreadLocal<Choreographer>() {
|
||||
@Override
|
||||
protected Choreographer initialValue() {
|
||||
Looper looper = Looper.myLooper();
|
||||
if (looper == null) {
|
||||
throw new IllegalStateException("The current thread must have a looper!");
|
||||
}
|
||||
return new Choreographer(looper, VSYNC_SOURCE_SURFACE_FLINGER);
|
||||
}
|
||||
};
|
||||
|
||||
// Enable/disable vsync for animations and drawing.
|
||||
private static final boolean USE_VSYNC = SystemProperties.getBoolean(
|
||||
"debug.choreographer.vsync", true);
|
||||
@@ -202,10 +218,12 @@ public final class Choreographer {
|
||||
|
||||
private static final int CALLBACK_LAST = CALLBACK_COMMIT;
|
||||
|
||||
private Choreographer(Looper looper) {
|
||||
private Choreographer(Looper looper, int vsyncSource) {
|
||||
mLooper = looper;
|
||||
mHandler = new FrameHandler(looper);
|
||||
mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;
|
||||
mDisplayEventReceiver = USE_VSYNC
|
||||
? new FrameDisplayEventReceiver(looper, vsyncSource)
|
||||
: null;
|
||||
mLastFrameTimeNanos = Long.MIN_VALUE;
|
||||
|
||||
mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
|
||||
@@ -233,6 +251,13 @@ public final class Choreographer {
|
||||
return sThreadInstance.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static Choreographer getSfInstance() {
|
||||
return sSfThreadInstance.get();
|
||||
}
|
||||
|
||||
/** Destroys the calling thread's choreographer
|
||||
* @hide
|
||||
*/
|
||||
@@ -816,8 +841,8 @@ public final class Choreographer {
|
||||
private long mTimestampNanos;
|
||||
private int mFrame;
|
||||
|
||||
public FrameDisplayEventReceiver(Looper looper) {
|
||||
super(looper);
|
||||
public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
|
||||
super(looper, vsyncSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -35,6 +35,23 @@ import java.lang.ref.WeakReference;
|
||||
* @hide
|
||||
*/
|
||||
public abstract class DisplayEventReceiver {
|
||||
|
||||
/**
|
||||
* When retrieving vsync events, this specifies that the vsync event should happen at the normal
|
||||
* vsync-app tick.
|
||||
* <p>
|
||||
* Needs to be kept in sync with frameworks/native/include/gui/ISurfaceComposer.h
|
||||
*/
|
||||
public static final int VSYNC_SOURCE_APP = 0;
|
||||
|
||||
/**
|
||||
* When retrieving vsync events, this specifies that the vsync event should happen whenever
|
||||
* Surface Flinger is processing a frame.
|
||||
* <p>
|
||||
* Needs to be kept in sync with frameworks/native/include/gui/ISurfaceComposer.h
|
||||
*/
|
||||
public static final int VSYNC_SOURCE_SURFACE_FLINGER = 1;
|
||||
|
||||
private static final String TAG = "DisplayEventReceiver";
|
||||
|
||||
private final CloseGuard mCloseGuard = CloseGuard.get();
|
||||
@@ -46,7 +63,7 @@ public abstract class DisplayEventReceiver {
|
||||
private MessageQueue mMessageQueue;
|
||||
|
||||
private static native long nativeInit(WeakReference<DisplayEventReceiver> receiver,
|
||||
MessageQueue messageQueue);
|
||||
MessageQueue messageQueue, int vsyncSource);
|
||||
private static native void nativeDispose(long receiverPtr);
|
||||
@FastNative
|
||||
private static native void nativeScheduleVsync(long receiverPtr);
|
||||
@@ -55,14 +72,16 @@ public abstract class DisplayEventReceiver {
|
||||
* Creates a display event receiver.
|
||||
*
|
||||
* @param looper The looper to use when invoking callbacks.
|
||||
* @param vsyncSource The source of the vsync tick. Must be on of the VSYNC_SOURCE_* values.
|
||||
*/
|
||||
public DisplayEventReceiver(Looper looper) {
|
||||
public DisplayEventReceiver(Looper looper, int vsyncSource) {
|
||||
if (looper == null) {
|
||||
throw new IllegalArgumentException("looper must not be null");
|
||||
}
|
||||
|
||||
mMessageQueue = looper.getQueue();
|
||||
mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue);
|
||||
mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,
|
||||
vsyncSource);
|
||||
|
||||
mCloseGuard.open("dispose");
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ static struct {
|
||||
class NativeDisplayEventReceiver : public DisplayEventDispatcher {
|
||||
public:
|
||||
NativeDisplayEventReceiver(JNIEnv* env,
|
||||
jobject receiverWeak, const sp<MessageQueue>& messageQueue);
|
||||
jobject receiverWeak, const sp<MessageQueue>& messageQueue, jint vsyncSource);
|
||||
|
||||
void dispose();
|
||||
|
||||
@@ -65,8 +65,9 @@ private:
|
||||
|
||||
|
||||
NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env,
|
||||
jobject receiverWeak, const sp<MessageQueue>& messageQueue) :
|
||||
DisplayEventDispatcher(messageQueue->getLooper()),
|
||||
jobject receiverWeak, const sp<MessageQueue>& messageQueue, jint vsyncSource) :
|
||||
DisplayEventDispatcher(messageQueue->getLooper(),
|
||||
static_cast<ISurfaceComposer::VsyncSource>(vsyncSource)),
|
||||
mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
|
||||
mMessageQueue(messageQueue) {
|
||||
ALOGV("receiver %p ~ Initializing display event receiver.", this);
|
||||
@@ -113,7 +114,7 @@ void NativeDisplayEventReceiver::dispatchHotplug(nsecs_t timestamp, int32_t id,
|
||||
|
||||
|
||||
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
|
||||
jobject messageQueueObj) {
|
||||
jobject messageQueueObj, jint vsyncSource) {
|
||||
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
|
||||
if (messageQueue == NULL) {
|
||||
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
|
||||
@@ -121,7 +122,7 @@ static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
|
||||
}
|
||||
|
||||
sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env,
|
||||
receiverWeak, messageQueue);
|
||||
receiverWeak, messageQueue, vsyncSource);
|
||||
status_t status = receiver->initialize();
|
||||
if (status) {
|
||||
String8 message;
|
||||
@@ -156,7 +157,7 @@ static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jlong receiverPtr) {
|
||||
static const JNINativeMethod gMethods[] = {
|
||||
/* name, signature, funcPtr */
|
||||
{ "nativeInit",
|
||||
"(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;)J",
|
||||
"(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;I)J",
|
||||
(void*)nativeInit },
|
||||
{ "nativeDispose",
|
||||
"(J)V",
|
||||
|
||||
@@ -33,8 +33,9 @@ namespace android {
|
||||
// using just a few large reads.
|
||||
static const size_t EVENT_BUFFER_SIZE = 100;
|
||||
|
||||
DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper) :
|
||||
mLooper(looper), mWaitingForVsync(false) {
|
||||
DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper,
|
||||
ISurfaceComposer::VsyncSource vsyncSource) :
|
||||
mLooper(looper), mReceiver(vsyncSource), mWaitingForVsync(false) {
|
||||
ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,8 @@ namespace android {
|
||||
|
||||
class DisplayEventDispatcher : public LooperCallback {
|
||||
public:
|
||||
DisplayEventDispatcher(const sp<Looper>& looper);
|
||||
DisplayEventDispatcher(const sp<Looper>& looper,
|
||||
ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp);
|
||||
|
||||
status_t initialize();
|
||||
void dispose();
|
||||
|
||||
@@ -668,7 +668,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
|
||||
|
||||
private final class HotplugDisplayEventReceiver extends DisplayEventReceiver {
|
||||
public HotplugDisplayEventReceiver(Looper looper) {
|
||||
super(looper);
|
||||
super(looper, VSYNC_SOURCE_APP);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -83,10 +83,6 @@ public class WindowAnimator {
|
||||
// check if some got replaced and can be removed.
|
||||
private boolean mRemoveReplacedWindows = false;
|
||||
|
||||
private long mCurrentFrameTime;
|
||||
private final Runnable mAnimationTick;
|
||||
private final SurfaceFlingerVsyncChoreographer mSfChoreographer;
|
||||
|
||||
private Choreographer mChoreographer;
|
||||
|
||||
/**
|
||||
@@ -95,40 +91,19 @@ public class WindowAnimator {
|
||||
*/
|
||||
private boolean mAnimationFrameCallbackScheduled;
|
||||
|
||||
/**
|
||||
* Indicates whether we have an animation tick scheduled. The tick is the thing that actually
|
||||
* executes the animation step, which will happen at vsync-sf.
|
||||
*/
|
||||
private boolean mAnimationTickScheduled;
|
||||
|
||||
WindowAnimator(final WindowManagerService service) {
|
||||
mService = service;
|
||||
mContext = service.mContext;
|
||||
mPolicy = service.mPolicy;
|
||||
mWindowPlacerLocked = service.mWindowPlacerLocked;
|
||||
AnimationThread.getHandler().runWithScissors(
|
||||
() -> mChoreographer = Choreographer.getInstance(), 0 /* timeout */);
|
||||
() -> mChoreographer = Choreographer.getSfInstance(), 0 /* timeout */);
|
||||
|
||||
// TODO: Multi-display: If displays have different vsync tick, have a separate tick per
|
||||
// display.
|
||||
mSfChoreographer = new SurfaceFlingerVsyncChoreographer(AnimationThread.getHandler(),
|
||||
mService.getDefaultDisplayContentLocked().getDisplay(), mChoreographer);
|
||||
mAnimationTick = () -> {
|
||||
synchronized (mService.mWindowMap) {
|
||||
mAnimationTickScheduled = false;
|
||||
}
|
||||
animate(mCurrentFrameTime);
|
||||
};
|
||||
mAnimationFrameCallback = frameTimeNs -> {
|
||||
synchronized (mService.mWindowMap) {
|
||||
mCurrentFrameTime = frameTimeNs;
|
||||
mAnimationFrameCallbackScheduled = false;
|
||||
if (mAnimationTickScheduled) {
|
||||
return;
|
||||
}
|
||||
mAnimationTickScheduled = true;
|
||||
}
|
||||
mSfChoreographer.scheduleAtSfVsync(mAnimationTick);
|
||||
animate(frameTimeNs);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -422,7 +397,7 @@ public class WindowAnimator {
|
||||
}
|
||||
|
||||
boolean isAnimationScheduled() {
|
||||
return mAnimationFrameCallbackScheduled || mAnimationTickScheduled;
|
||||
return mAnimationFrameCallbackScheduled;
|
||||
}
|
||||
|
||||
Choreographer getChoreographer() {
|
||||
|
||||
Reference in New Issue
Block a user