WMS中Choreographer 配合 VSYNC 中断信号
- 1、了解SurfaceFlinger中VSYNC信号刷新
- 2、Choreographer 舞蹈编导
- 2.1 Choreographer初始化
- 2.2 FrameHandler中处理任务
- 2.3 FrameDisplayEventReceiver初始化
- 3.4 简易流程图
- 3、ViewRootImpl中scheduleTraversals
- 3.1 postCallback 通过nativeScheduleVsync(mReceiverPtr)申请 VSYNC 信号
- 3.2 接收 `Vsync信号` 事件回调 `onVsync` 执行`doFrame`
- 3.3 Choreographer.CALLBACK_TRAVERSAL执行
- 4、简易时序图
- * 参考相关 Choreographer、Vsync
Activity窗口的添加过程
WMS侧添加界面
WindowManager中Surface申请流程
android12-release
1、了解SurfaceFlinger中VSYNC信号刷新
AOSP > 文档 > 核心主题 > VSYNC
Vsync 信号可以由硬件产生,也可以用软件模拟。 60 fps画面每秒更新60次,大部分手机屏幕的刷新率,都维持在60 HZ,
通常就是 Android 的渲染机制是 16.67 ms 绘制一次, 60hz 的屏幕也是 16.67 ms 刷新一次
看几张图片了解:
2、Choreographer 舞蹈编导
Choreographer 扮演 Android 渲染链路中承上启下的角色:
- 承上:负责接收和处理 App 的各种更新消息和回调,等到
Vsync
到来的时候统一处理。比如集中处理Input
(主要是 Input 事件的处理) 、Animation
(动画相关)、Traversal
(包括measure
、layout
、draw
等操作) ,判断卡顿掉帧情况,记录 CallBack 耗时等;- 启下:负责请求和接收
Vsync
信号。接收Vsync
事件回调(通过FrameDisplayEventReceiver.onVsync
),请求Vsync
(FrameDisplayEventReceiver.scheduleVsync
) 。
2.1 Choreographer初始化
FrameHandler(looper)
:在Choreographer 的所有任务最终都会发送到该 Looper 所在的线程。mDisplayEventReceiver
是否开启VSYNC(USE_VSYNC默认true
),开启VSYNC后将通过FrameDisplayEventReceiver
接受VSYNC脉冲。
- 接收
Vsync
事件回调(通过FrameDisplayEventReceiver.onVsync
)- 请求
Vsync
(FrameDisplayEventReceiver.scheduleVsync
)
3.mLastFrameTimeNanos
上一次帧绘制时间点;,屏幕刷新频率是60Hz的,这里是纳秒 ≈16000000ns 还是16ms (被方法mFrameIntervalNanos
计算一帧的时间android.view.Display#getRefreshRate
替代)
4.mCallbackQueues
中存放要执行的输入、动画、遍历绘制等任务;也就是CALLBACK_INPUT
、CALLBACK_ANIMATION
、CALLBACK_INSETS_ANIMATION
、CALLBACK_TRAVERSAL
、CALLBACK_COMMIT
。
private Choreographer(Looper looper, int vsyncSource) {
mLooper = looper;
mHandler = new FrameHandler(looper);
mDisplayEventReceiver = USE_VSYNC
? new FrameDisplayEventReceiver(looper, vsyncSource)
: null;
mLastFrameTimeNanos = Long.MIN_VALUE;
mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
for (int i = 0; i <= CALLBACK_LAST; i++) {
mCallbackQueues[i] = new CallbackQueue();
}
// b/68769804: For low FPS experiments.
setFPSDivisor(SystemProperties.getInt(ThreadedRenderer.DEBUG_FPS_DIVISOR, 1));
}
2.2 FrameHandler中处理任务
MSG_DO_FRAME
如果启用VSYNC机制,当VSYNC信号到来时触发处理doFrame()
MSG_DO_SCHEDULE_VSYNC
申请VSYNC信号,例如当前需要绘制任务时doScheduleVsync()
MSG_DO_SCHEDULE_CALLBACK
需要延迟的任务,最终还是执行上述两个事件
private final class FrameHandler extends Handler {
public FrameHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DO_FRAME:
doFrame(System.nanoTime(), 0, new DisplayEventReceiver.VsyncEventData());
break;
case MSG_DO_SCHEDULE_VSYNC:
doScheduleVsync();
break;
case MSG_DO_SCHEDULE_CALLBACK:
doScheduleCallback(msg.arg1);
break;
}
}
}
2.3 FrameDisplayEventReceiver初始化
FrameDisplayEventReceiver
继承DisplayEventReceiver
,通过JNI
对应android_view_DisplayEventReceiver.cpp#NativeDisplayEventReceiver
(即DisplayEventDispatcher.cpp
)- 重载
FrameDisplayEventReceiver#onVsync
接收接收Vsync信号
事件回调;而请求Vsync信号
调用DisplayEventReceiver.java#scheduleVsync
(最终调用DisplayEventDispatcher.cpp#scheduleVsync()
)
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
private boolean mHavePendingVsync;
private long mTimestampNanos;
private int mFrame;
private VsyncEventData mLastVsyncEventData = new VsyncEventData();
public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
super(looper, vsyncSource, 0);
}
// TODO(b/116025192): physicalDisplayId is ignored because SF only emits VSYNC events for
// the internal display and DisplayEventReceiver#scheduleVsync only allows requesting VSYNC
// for the internal display implicitly.
@Override
public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
VsyncEventData vsyncEventData) {
try {
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW,
"Choreographer#onVsync " + vsyncEventData.id);
}
// Post the vsync event to the Handler.
// The idea is to prevent incoming vsync events from completely starving
// the message queue. If there are no messages in the queue with timestamps
// earlier than the frame time, then the vsync event will be processed immediately.
// Otherwise, messages that predate the vsync event will be handled first.
long now = System.nanoTime();
if (timestampNanos > now) {
Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f)
+ " ms in the future! Check that graphics HAL is generating vsync "
+ "timestamps using the correct timebase.");
timestampNanos = now;
}
if (mHavePendingVsync) {
Log.w(TAG, "Already have a pending vsync event. There should only be "
+ "one at a time.");
} else {
mHavePendingVsync = true;
}
mTimestampNanos = timestampNanos;
mFrame = frame;
mLastVsyncEventData = vsyncEventData;
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
@Override
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame, mLastVsyncEventData);
}
}
3.4 简易流程图
3、ViewRootImpl中scheduleTraversals
以 View 的绘制流程为例,从 ViewRootImpl 的 scheduleTraversals 方法开始,其内部通过 Choreographer 的 postCallback 将绘制任务添加到 Chorographer
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
3.1 postCallback 通过nativeScheduleVsync(mReceiverPtr)申请 VSYNC 信号
- Choreographer 提供了两种添加任务的方式:
postCallback()
和postFrameCallback()
。最终都调用postCallbackDelayedInternal
public void postCallbackDelayed(int callbackType,
Runnable action, Object token, long delayMillis) {
if (action == null) {
throw new IllegalArgumentException("action must not be null");
}
if (callbackType < 0 || callbackType > CALLBACK_LAST) {
throw new IllegalArgumentException("callbackType is invalid");
}
postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}
public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
if (callback == null) {
throw new IllegalArgumentException("callback must not be null");
}
postCallbackDelayedInternal(CALLBACK_ANIMATION,
callback, FRAME_CALLBACK_TOKEN, delayMillis);
}
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
根据任务类型 callbackType 添加到对应的 CallbackQueue 内mDisplayEventReceiver.scheduleVsync()
通过JNInativeScheduleVsync(mReceiverPtr)
调用到DisplayEventDispatcher.cpp#scheduleVsync()
申请 VSYNC 信号。
3.2 接收 Vsync信号
事件回调 onVsync
执行doFrame
- FrameDisplayEventReceiver 实现了 Runnable,将其作为
callback
发送到 FrameHandler,此时run
方法便得到执行并且执行doFrame
方法frameTimeNanos < mLastFrameTimeNanos
未知原因,居然小于最后一帧的时间,重新申请VSYNC信号scheduleVsyncLocked()
doCallbacks()
按照类型顺序触发 doCallbacks 回调相关任务,类型顺序Choreographer.CALLBACK_INPUT
->Choreographer.CALLBACK_ANIMATION
、Choreographer.CALLBACK_INSETS_ANIMATION
、Choreographer.CALLBACK_TRAVERSAL
、Choreographer.CALLBACK_COMMIT
void doFrame(long frameTimeNanos, int frame,
DisplayEventReceiver.VsyncEventData vsyncEventData) {
final long startNanos;
final long frameIntervalNanos = vsyncEventData.frameInterval;
try {
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW,
"Choreographer#doFrame " + vsyncEventData.id);
}
synchronized (mLock) {
if (!mFrameScheduled) {
traceMessage("Frame not scheduled");
return; // no work to do
}
if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) {
mDebugPrintNextFrameTimeDelta = false;
Log.d(TAG, "Frame time delta: "
+ ((frameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + " ms");
}
long intendedFrameTimeNanos = frameTimeNanos;
startNanos = System.nanoTime();
final long jitterNanos = startNanos - frameTimeNanos;
if (jitterNanos >= frameIntervalNanos) {
final long skippedFrames = jitterNanos / frameIntervalNanos;
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");
}
final long lastFrameOffset = jitterNanos % frameIntervalNanos;
if (DEBUG_JANK) {
Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
+ "which is more than the frame interval of "
+ (frameIntervalNanos * 0.000001f) + " ms! "
+ "Skipping " + skippedFrames + " frames and setting frame "
+ "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
}
frameTimeNanos = startNanos - lastFrameOffset;
}
if (frameTimeNanos < mLastFrameTimeNanos) {
if (DEBUG_JANK) {
Log.d(TAG, "Frame time appears to be going backwards. May be due to a "
+ "previously skipped frame. Waiting for next vsync.");
}
traceMessage("Frame time goes backward");
scheduleVsyncLocked();
return;
}
if (mFPSDivisor > 1) {
long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
if (timeSinceVsync < (frameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
traceMessage("Frame skipped due to FPSDivisor");
scheduleVsyncLocked();
return;
}
}
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, vsyncEventData.id,
vsyncEventData.frameDeadline, startNanos, vsyncEventData.frameInterval);
mFrameScheduled = false;
mLastFrameTimeNanos = frameTimeNanos;
mLastFrameIntervalNanos = frameIntervalNanos;
mLastVsyncEventData = vsyncEventData;
}
AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
mFrameInfo.markInputHandlingStart();
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos, frameIntervalNanos);
mFrameInfo.markAnimationsStart();
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos, frameIntervalNanos);
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos,
frameIntervalNanos);
mFrameInfo.markPerformTraversalsStart();
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos, frameIntervalNanos);
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos, frameIntervalNanos);
} finally {
AnimationUtils.unlockAnimationClock();
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
if (DEBUG_FRAMES) {
final long endNanos = System.nanoTime();
Log.d(TAG, "Frame " + frame + ": Finished, took "
+ (endNanos - startNanos) * 0.000001f + " ms, latency "
+ (startNanos - frameTimeNanos) * 0.000001f + " ms.");
}
}
3.3 Choreographer.CALLBACK_TRAVERSAL执行
scheduleTraversals()可以查看WindowManager中Surface申请流程
performMeasure/performLayout/performDraw
最终分别调用到内部view的measure/layout/draw
(都是从界面View控件树的根节点DecorView出发,递归遍历整个View控件树,完成对整个View控件树的measure/layout/draw
操作)- 例如
draw方法
:开启硬件加速绘制执行mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this)
,最终还是执行View的draw开始;否则执行drawSoftware()
- 硬件加速绘制
updateRootDisplayList
从DecorView根节点出发,递归遍历View控件树,记录每个View节点的绘制操作命令,完成绘制操作命令树的构建。
private boolean draw(boolean fullRedrawNeeded) {
Surface surface = mSurface;
if (!surface.isValid()) {
return false;
}
if (DEBUG_FPS) {
trackFPS();
}
if (!sFirstDrawComplete) {
synchronized (sFirstDrawHandlers) {
sFirstDrawComplete = true;
final int count = sFirstDrawHandlers.size();
for (int i = 0; i< count; i++) {
mHandler.post(sFirstDrawHandlers.get(i));
}
}
}
scrollToRectOrFocus(null, false);
if (mAttachInfo.mViewScrollChanged) {
mAttachInfo.mViewScrollChanged = false;
mAttachInfo.mTreeObserver.dispatchOnScrollChanged();
}
boolean animating = mScroller != null && mScroller.computeScrollOffset();
final int curScrollY;
if (animating) {
curScrollY = mScroller.getCurrY();
} else {
curScrollY = mScrollY;
}
if (mCurScrollY != curScrollY) {
mCurScrollY = curScrollY;
fullRedrawNeeded = true;
if (mView instanceof RootViewSurfaceTaker) {
((RootViewSurfaceTaker) mView).onRootViewScrollYChanged(mCurScrollY);
}
}
final float appScale = mAttachInfo.mApplicationScale;
final boolean scalingRequired = mAttachInfo.mScalingRequired;
final Rect dirty = mDirty;
if (mSurfaceHolder != null) {
// The app owns the surface, we won't draw.
dirty.setEmpty();
if (animating && mScroller != null) {
mScroller.abortAnimation();
}
return false;
}
if (fullRedrawNeeded) {
dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
}
if (DEBUG_ORIENTATION || DEBUG_DRAW) {
Log.v(mTag, "Draw " + mView + "/"
+ mWindowAttributes.getTitle()
+ ": dirty={" + dirty.left + "," + dirty.top
+ "," + dirty.right + "," + dirty.bottom + "} surface="
+ surface + " surface.isValid()=" + surface.isValid() + ", appScale:" +
appScale + ", width=" + mWidth + ", height=" + mHeight);
}
mAttachInfo.mTreeObserver.dispatchOnDraw();
int xOffset = -mCanvasOffsetX;
int yOffset = -mCanvasOffsetY + curScrollY;
final WindowManager.LayoutParams params = mWindowAttributes;
final Rect surfaceInsets = params != null ? params.surfaceInsets : null;
if (surfaceInsets != null) {
xOffset -= surfaceInsets.left;
yOffset -= surfaceInsets.top;
// Offset dirty rect for surface insets.
dirty.offset(surfaceInsets.left, surfaceInsets.top);
}
boolean accessibilityFocusDirty = false;
final Drawable drawable = mAttachInfo.mAccessibilityFocusDrawable;
if (drawable != null) {
final Rect bounds = mAttachInfo.mTmpInvalRect;
final boolean hasFocus = getAccessibilityFocusedRect(bounds);
if (!hasFocus) {
bounds.setEmpty();
}
if (!bounds.equals(drawable.getBounds())) {
accessibilityFocusDirty = true;
}
}
mAttachInfo.mDrawingTime =
mChoreographer.getFrameTimeNanos() / TimeUtils.NANOS_PER_MS;
boolean useAsyncReport = false;
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty || mNextDrawUseBlastSync) {
if (isHardwareEnabled()) {
// If accessibility focus moved, always invalidate the root.
boolean invalidateRoot = accessibilityFocusDirty || mInvalidateRootRequested;
mInvalidateRootRequested = false;
// Draw with hardware renderer.
mIsAnimating = false;
if (mHardwareYOffset != yOffset || mHardwareXOffset != xOffset) {
mHardwareYOffset = yOffset;
mHardwareXOffset = xOffset;
invalidateRoot = true;
}
if (invalidateRoot) {
mAttachInfo.mThreadedRenderer.invalidateRoot();
}
dirty.setEmpty();
// Stage the content drawn size now. It will be transferred to the renderer
// shortly before the draw commands get send to the renderer.
final boolean updated = updateContentDrawBounds();
if (mReportNextDraw) {
// report next draw overrides setStopped()
// This value is re-sync'd to the value of mStopped
// in the handling of mReportNextDraw post-draw.
mAttachInfo.mThreadedRenderer.setStopped(false);
}
if (updated) {
requestDrawWindow();
}
useAsyncReport = true;
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
} else {
// If we get here with a disabled & requested hardware renderer, something went
// wrong (an invalidate posted right before we destroyed the hardware surface
// for instance) so we should just bail out. Locking the surface with software
// rendering at this point would lock it forever and prevent hardware renderer
// from doing its job when it comes back.
// Before we request a new frame we must however attempt to reinitiliaze the
// hardware renderer if it's in requested state. This would happen after an
// eglTerminate() for instance.
if (mAttachInfo.mThreadedRenderer != null &&
!mAttachInfo.mThreadedRenderer.isEnabled() &&
mAttachInfo.mThreadedRenderer.isRequested() &&
mSurface.isValid()) {
try {
mAttachInfo.mThreadedRenderer.initializeIfNeeded(
mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
} catch (OutOfResourcesException e) {
handleOutOfResourcesException(e);
return false;
}
mFullRedrawNeeded = true;
scheduleTraversals();
return false;
}
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
scalingRequired, dirty, surfaceInsets)) {
return false;
}
}
}
if (animating) {
mFullRedrawNeeded = true;
scheduleTraversals();
}
return useAsyncReport;
}
4、简易时序图
* 参考相关 Choreographer、Vsync
Choreographer原理
Android Systrace 基础知识(7) - Vsync 解读
直面底层技术: Android 之 VSYNC、 Choreographer 起源!