VSync及UI平滑技术
2016-05-09 19:54
495 查看
VSync及UI平滑技术
Android4.1中一个很大的改进是UI动画显得更平滑流畅。其中的关键技术就是vsync timing和triple buffer。1 Android4.1新画图特点
1.1 VSync的作用上面是一张没有Vsync控制的画图过程:当frame0在屏幕上显示时,frame1先在CPU里准备,再在GPU中render到显示内存,最后在下一个VSYN来到时,把frame1切换到屏幕。由于在Android4.1(JellyBean)之前,所有的绘制画图都是以一种“自由的,松散的”的方式调用,这样开始调用画图的时间不定(就是调用View::onDraw()的时间不定),所以当系统负荷很重时,frame2的onDraw()方法可能很晚才调用,在VSyn信号来时,Frame2还没有准备好,显示只好在显示frame1了。造成画面停顿。
引入VSYN就是解决这个问题。其实显示系统一直是用Vsync来切换图像的,只有在VSync信号下,显示内存的图像才会切换到屏幕。在JellyBean中,VSYNC被引入到上层的View绘图。View::onDraw()保证在VSync来到时被调用。示意图如下:
这样View::onDraw()保证在VSync信号到达时调用,避免的延迟。
其实用VSync只解决了View::onDraw()延时调用的问题。如果CPU或GPU画图的时间超过16ms(两个VSync信号的间隔),那一样会使两个连续的VSync显示一个frame,造成画面停顿。
2 、Android4.1对新UI画图机制的实现
2.1、 VSync信号的传递2.1.1 、SurfaceFlinger收到Vsync,转发到有画图请求的客户App。
vsync是一个周期产生的信号,它可以由软件模拟产生,也可以图像处理器硬件产生。由图像处理器产生时,图像处理器的驱动会在render图像到屏幕时,产生一个回调,上层软件利用这个回调为下一帧做准备。有关代码在frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cpp。
Vsync回调向上传递的第一层是SurfaceFlinger的EventThread::onVSyncReceived()
点击(此处)折叠或打开
void EventThread::onVSyncReceived(int, nsecs_t timestamp) {
Mutex::Autolock _l(mLock);
mVSyncTimestamp = timestamp;
mCondition.broadcast();
}
这个函数唤醒EventThread::threadLoop(),这个函数检查displayEventConnections中的count是否大于0,大于0表示这个
connection有画图的请求。有绘图请求的connection就调用
点击(此处)折叠或打开
status_t err = conn->postEvent(vsync);
通知对应的connection对端VSYNC到,可以绘制了。
注意每一个需要绘图的vsync都是一个vsync event
点击(此处)折叠或打开
// dispatch vsync events to listeners…
vsync.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
vsync.header.timestamp = timestamp;
vsync.vsync.count = mDeliveredEvents;
它们共享一个时间戳。这样使所有的绘图基于一个基点。
2.1.2、应用端的接受和处理
画图App用readLastVsyncMessage()接受VSYNC,通过下面的路径传递到用户的画图回调代码:
1)NativeDisplayEventReceiver::handleEvent()
2)dispatchVsync (DisplayEventReceiver.java)
3)FrameDisplayEventReceiver::onVsync(Choreographer.java)
4)FrameDisplayEventReceiver::doFrame()调用下面三个函数:
4.1)doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
4.2)doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
4.3)doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos)
三个队列里的画图操作被调用后。这些操作就从队列中删除。
3 UI绘制卡顿检测原理
android4.1起,Choreographer#FrameDisplayEventReceiver类用来接收VSYNC信号。onVsync中第一个参数表示VSync信号时间,timestampNanos表示应该开始准备绘制一帧数据。如果手机出现卡顿,onVsync函数会在timestampNanos+delayTime才会接受到VSync信号。private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable { public FrameDisplayEventReceiver(Looper looper) { super(looper); } @Override public void onVsync(long timestampNanos, int builtInDisplayId, int frame){ if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) { Log.d(TAG, "Received vsync from secondary display, but we don't support " + "this case yet. Choreographer needs a way to explicitly request " + "vsync for a specific display to ensure it doesn't lose track " + "of its scheduled vsync."); scheduleVsync(); return; } } }
如果onVsync函数被调用时间比Vsync时间延迟时间超过一定时间(假设15帧个Vsync时间间隔),则判定当前UI绘制出现卡顿。很有可能因为UI绘制一帧时间大于1000/60 ms。
try { mFrameIntervalNanos = (Long) Choreographer.class.getDeclaredField("mFrameIntervalNanos").get(Choreographer.getInstance()); } catch (Exception e) { mFrameIntervalNanos = 1000000000 / 60; } mTolerableSkippedNanos = 15 * mFrameIntervalNanos; long startNanos = System.nanoTime(); jitterNanos = startNanos - timestampNanos; if (jitterNanos >= mTolerableSkippedNanos) { final long skippedFrames = jitterNanos / mFrameIntervalNanos; Log.i(TAG, "main thread skip " + skippedFrames + "frames"); }
相关文章推荐
- iOS学习UI之UINavigationController
- UI控件为什么要用weak
- 安卓UI构架解读1
- UIBezierPath贝塞尔弧线常用方法记
- Android 中的消息模型(Message,MessageQueue,handle,looper,)
- Ligerui树操作
- UITabbarController使用(二)隐藏和显示tabbar
- iOS5,iOS6,iOS7中UITabBarController,UINavigationController等界面中的旋转问题总结
- UIImagePickerController的详细使用
- 【从零开始学NGUI 】 (十二)UIGrid
- iOS UIKit:CollectionView之布局(2)
- @RequestParam @RequestBody @PathVariable 等参数绑定注解详解
- 将request.getParameterMap()转换成可操作的普通Map
- poj 2778 DNA Sequence ac自动机+矩阵快速幂
- iOS UISegmentedControl设置指定索引选项不可选
- Web Service单元测试工具实例介绍之SoapUI
- iOS UIAlertController的使用
- [iOS-UI]给输入框添加清除按钮的代码
- 百度编辑器UEditor常用设置函数大全
- error: lvalue required as unary ‘&’ operand