- 收藏
- 点赞
- 分享
- 举报
图形界面中framebuffer
双buffer与单buffer 在嵌入式平台Linux,主要通过framebuffer来显示UI。FrameBuffer实际上就是嵌入式系统中专门为GPU所保留的一块连续的物理内存,LED通过专门的总线从framebuffer读取数据,显示到屏幕上。 根据系统中framebuffer的数量,可以分成单buffer和双buffer两种。 先来说说单buffer: CPU往framebuffer上写,LED从framebuffer读,这是两个同时进行的过程,需要在时间上配合,否则会出现问题。 如果CPU往framebuffer上写的速度>LED从framebuffer读的速度,那么就有可能出现LED在一行一行的读取前一屏数据的时候,CPU却已经刷新了整屏,从而导致显示混乱。这里要注意,LED从framebuffer读的速度并不等于屏幕的刷新频率,如果刷新频率为60hz,那么很有可能LED花了3个ms去读,剩余的时间都在等待。应该说CPU往framebuffer写的速度>LED从framebuffer读的速度还是很困难的。 如果CPU往framebuffer写的速度太慢,也会出现屏幕闪烁的问题。比如说要画一幅图,CPU首先将其填充为白色,这时LED刷新,屏幕显示为白色,之后开始画完其他内容,屏幕正常显示。这时给用户的感觉就是屏幕一闪。这就要求CPU尽快的画完一屏,尽量保证写一屏不要跨越LED刷新周期。 因此,在单framebuffer的时代,为了防止屏幕出现闪烁,我们一般是在内存中开辟一块与framebuffer同样大小的内容,将屏幕的内容都写好,然后再执行一次内存拷贝。从而使写framebuffer的时间尽可能的短。 但这种机制有问题,我以屏幕分辩率为320240为例。 一块framebuffer的大小为:3202404=0.3072M。 也就是说,我要先在内存中填写0.3M的内存,然后再把这块内存拷贝到framebuffer中。为了简单起见,这里我举一个将屏幕置为白色的例子,排除屏幕内容计算的影响。 显示的实际过程为,将内存中0.3M置为0,然后读取这0.3M的内存,拷贝到Framebuffer中。 在我使用的嵌入式平台中,采用的是SDRAM,532Mhz的ARM11芯片。通过使用lmbench得到内存访问的速率为: Local* Communication bandwidths in MB/s - bigger is better
Host OS Pipe AF TCP File Mmap Bcopy Bcopy Mem Mem UNIX reread reread (libc) (hand) read write
phone Linux 2.6 79.1 94.7 13.3 47.1 135.9 78.6 77.8 135. 205.9 很奇怪,为什么读会比写慢,可能与cache有关,这里先不用去管它。 下面我来计算单buffer所使用的时间。 将0.3M内存置为0:0.3072×1000/200=1.5ms。 读0.3M内存:0.3072×1000/135=2.7ms 将03M内存写到framebuffer:0:0.3072×1000/200=1.5ms。 总共时间为5.7ms。实际的传输情况要比这个多,因为还要涉及循环的开销。 5.7ms看起来还行。 当屏幕分辨率变大时,情况就不一样了。如果屏幕的分辨率扩大为800600,那么其数据量将是320240的6.25倍。那么单纯的显示一个白屏的时间,将为5.76.25=36.6ms,也就是说如果只是显示白屏的话,我们也最多显示30帧,这个时间实在是太长了。 并且如果我们只计算从内存拷贝到framebuffer的时间,其为 (2.7+1.5)6.25=25.25ms。如何LED刷新频率为60hz,那么刷新一屏的时间将为18ms,这样即使单纯的从内存拷贝到framebuffer,写一屏的时间也大于LED的刷新频率,肯定会造成屏幕的闪烁。 单buffer的另外一个问题在于,其每次都要进行内存拷贝,这样其会重新刷新CPU中的数据cache,对软件的性能有不利的影响。 性能的改善: 从软件的角度来讲,每次尽量缩小刷新的区域,减少内存的传输。 从硬件角度来讲,采用更高的CPU,采用更快的DDR内存,提高内存的吞吐率。
下面再来说下双buffer。 双buffer的好处在于,在LED显示当前framebuffer时,软件可以直接向后台的framebuffer写,在写完之后只是两个buffer做下切换即可,不再需要内存拷贝,从而提高效率。 还是以显示一个320×240的白屏为例,其过程变成: 1、 直接向后面的framebuffer写,其花费时间为0.3072×1000/200=1.5ms。 2、 写完之后framebffer切换,这个时间可以忽略不计。 那么当屏幕变为800×600时,其所花费的时间为1.5*6.25=9.375,要比单buffer的效率提高2.6倍。 并且双buffer还有一个好处,由于其没有了内存拷贝的环节,其对数据cache的影响较小,对于提高软件性能有利。另外给framebuffer的数据置位,其必然要经过Cache,由于这些数据写完后不再使用,那么使用写透cache要比使用回写式cache效率高。
双buffer还有一个好处,就是可以利用GPU的硬加速。如果要利用GPU的硬加速,则显示的数据必须是在预留的物理内存中,这在单buffer机制中是不能实现的。
双buffer也有一个致命的弱点:那就是局部刷新。 在单buffer的情况下,很容易实现局部刷新,只需要把局部的数据拷贝到对应的位置就可以了。而双buffer来讲,如果要实现局部刷新,则必须先将当前显示的buffer数据拷贝到后面的buffer,然后再做局部刷新。这样对于双buffer的设备来讲,刷新整屏的效率要比局部刷新更高。
局部刷新的理想情况: 将局部要修改的数据,直接写到另外一个显存中,与前buffer做叠加。
应该说单buffer和双buffer各有擅长,关键在于如何灵活运用。
直写式(write through),也叫写透,即CPU在向Cache写入数据的同时,也把数据写入主存以保证Cache和主存中相应单元数据的一致性,其特点是简单可靠,但由于CPU每次更新时都要对主存写入,速度必然受影响。 回写式(write back),即CPU只向Cache写入,并用标记加以注明,直到Cache中被写过的块要被进入的信息块取代时,才一次性写入主存。这种方式考虑到写入的往往是中间结果,每次写入主存速度慢而且不必要。其特点是速度快,避免了不必要的冗余写操作,但结构上较复杂。 直写式和回写式有着截然不同的操作,在不同的场合,不同的内存块使用不同的回写策略(如果你的系统可以实现的话)要比使用一种策略要高效得多。具体一点,对于反复存取的内存块置成写回,而把一次写入而很长时间以后再使用的内存置为写透,可以大大提高Cache的效率。 第一点很容易理解,第二点就需要琢磨一下了,由于写透的操作是,当缓存有该地址的数据时同时更新缓存和主存,当缓存没有该地址数据直接写主存,忽略缓存。当该地址的数据很长时间后才被使用到,那么在使用的时候该数据肯定不在Cache中(被替换了),所以不如直接写入主存来得直接; 相反,如果使用写回操作,当 Cache中有该地址数据,需要更新该数据,设置dirty位,很长时间后再使用该数据或被替换的时候才将其刷进主存,这样白白的占据了宝贵的Cache;而当 Cache没有该地址数据时,情况更糟糕,首先需要将相应的主存数据(一个Cache line)导入Cache,再更新数据,设置dirty位,再等待被刷回内存,这种情况不仅占用了Cache的空间,还多一次从主存中导入数据的过程,同样占据总线,开销很大。至于为什么要先从主存中导入数据,是因为Cache往主存回写数据时是按照一个Cache line 单位来写的,但被更新的数据可能没有一个Cache line这么多,所以为了保证数据一致性,必须先把数据导入Cache,更新后再刷回来。 对于很多视频解码来说,帧写入过程是一个一次性的动作,只有在下一次作为参考帧时才会被使用到,所以帧缓冲内存可以设置为写透操作,而下一次使用它的时候很可能是作为参考帧来使用,而作为参考帧不需要反复的存取,只需一次读操作就可以了,所以效率并不会因为不经过Cache而降低。实验证明该方法可以使 mpeg4 sp解码提高20-30%的效率。
Markdown 语法
- 加粗**内容**
- 斜体*内容*
- 删除线~~内容~~
- 引用> 引用内容
- 代码`代码`
- 代码块```编程语言↵代码```
- 链接[链接标题](url)
- 无序列表- 内容
- 有序列表1. 内容
- 缩进内容
- 图片![alt](url)
-
2019-06-15 17:35:25
-
2018-09-02 11:02:55
-
2020-10-10 10:07:46
-
2018-10-20 11:34:56
-
2018-10-19 11:08:36
-
2016-05-27 13:57:40
-
2020-12-25 14:41:49
-
2021-06-21 16:42:51
-
2016-09-01 11:42:18
-
2016-04-18 15:54:15
-
2014-01-15 13:53:07
-
2019-01-27 12:10:20
-
2020-06-02 19:26:07
-
2012-12-24 14:21:01
-
12008-08-01 19:43:05
-
2016-10-20 11:51:25
-
2019-09-12 11:10:24
-
2016-10-27 10:13:22
-
02008-08-01 19:47:15
-
50帮忙解决个交叉编译的问题
-
20帮忙交叉编译个源码
-
5Hi3516CV610 如何使用SD卡升级固件
-
5cat /dev/logmpp 报错 <3>[ vi] [func]:vi_send_frame_node [line]:99 [info]:vi pic queue is full!
-
50如何获取vpss chn的图像修改后发送至vo
-
5FPGA通过Bt1120传YUV422数据过来,vi接收不到数据——3516dv500
-
50SS928 运行PQtools 拼接 推到设备里有一半画面会异常
-
53536AV100的sample_vdec输出到CVBS显示
-
10海思板子mpp怎么在vi阶段改变视频数据尺寸
-
10HI3559AV100 多摄像头同步模式
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明