Chinaunix首页 | 论坛 | 博客
  • 博客访问: 122831
  • 博文数量: 29
  • 博客积分: 652
  • 博客等级: 上士
  • 技术积分: 340
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-03 21:26
文章分类

全部博文(29)

文章存档

2013年(8)

2012年(21)

分类: 嵌入式

2012-07-12 17:26:07

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,造成画面停顿。
如上图,GPU处理frameB的时间过长,导致VSync来到时,frameB没有render完成。

1.2 Tipple Buffer的示意
三缓冲(Tripple buffer)的机制就是解决这个问题。
引入BufferC。新的VSync来到时,并且bufferB还在使用时,新的bufferC被引入。画图在新的bufferC中进行,BufferB被拖后到第三帧显示,整个UI显示时间被拖后一帧。但每一帧都按一定的顺序一定的时间片显示。这就是tripple buffer的原理。

上面的图片来自。

2 、Android4.1对新UI画图机制的实现

2.1、 VSync信号的传递

2.1.1 、SurfaceFlinger收到Vsync,转发到有画图请求的客户App。
vsync是一个周期产生的信号,它可以由软件模拟产生,也可以图像处理器硬件产生。由图像处理器产生时,图像处理器的驱动会在render图像到屏幕时,产生一个回调,上层软件利用这个回调为下一帧做准备。有关代码在/////。

Vsync回调向上传递的第一层是SurfaceFlinger的EventThread::onVSyncReceived()

点击(此处)折叠或打开

  1. void EventThread::onVSyncReceived(int, nsecs_t timestamp) {
  2.     Mutex::Autolock _l(mLock);
  3.     mVSyncTimestamp = timestamp;
  4.     mCondition.broadcast();
  5. }
这个函数唤醒
::(),这个函数检查中的count是否大于0,大于0表示这个
connection有画图的请求。有绘图请求的connection就调用

点击(此处)折叠或打开

  1. status_t err = conn->postEvent(vsync);

通知对应的connection对端VSYNC到,可以绘制了。
注意每一个需要绘图的vsync都是一个vsync event 

点击(此处)折叠或打开

  1. // dispatch vsync events to listeners...
  2. vsync.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
  3. vsync.header.timestamp = timestamp;
  4. vsync.vsync.count = mDeliveredEvents;
它们共享一个时间戳。这样使所有的绘图基于一个基点。

2.1.2、应用端的接受和处理
画图App用readLastVsyncMessage()接受VSYNC,通过下面的路径传递到用户的画图回调代码:

  1. 1)NativeDisplayEventReceiver::handleEvent()
  2. 2)dispatchVsync (DisplayEventReceiver.java)
  3. 3)FrameDisplayEventReceiver::onVsync(Choreographer.java)
  4. 4)FrameDisplayEventReceiver::doFrame()调用下面三个函数:
  5.  4.1)doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
  6.  4.2)doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
  7.  4.3)doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos)
三个队列里的画图操作被调用后。这些操作就从队列中删除。

2.1.3 应用请求VSYNC事件
如果一个应用没有请求VSyn事件,surfaceflinger不会给这个应用发VSync事件,那么应用的画图代码永远也不会调用。所以应用必须向surfaceflinger请求VSync。

应用请求VSYNC就是调用(),它通过下面的调用链传递到IDisplayEventConnection

  1. 1) NativeDisplayEventReceiver::scheduleVsync()
  2. 2) DisplayEventReceiver::requestNextVsync()
  3. 3) IDisplayEventConnection::requestNextVsync()

最终到达surfaceflinger的 void ::::()
在这里connection的count=0,这样在下次VSync来时,::()就可以向对应的connection发Vsync事件了。如1.1节的描述。

2.2 Tripple Buffer

比较ICS中::()和JellyBean的::()没有大的区别,在ICS已经实现的Tripple buffer




阅读(10709) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~