免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3918 | 回复: 0
打印 上一主题 下一主题

[20100323]Android双Framebuffer机制 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-12-22 08:51 |只看该作者 |倒序浏览
Android double framebuffer(how to set)
 
 本文来自http://www.yuan.se/?p=7
 
Patch Framebuffer Driver of PrimeCell Color LCD Controller with Double-Buffering to Support Android’s Page-Flipping
支持Android页交换的PrimeCell Color LCD控制器Framebuffer驱动补丁
参考网址:http://pdk.android.com/online-pdk/guide/display_drivers.html 
Brief introduction

Android uses a screen composition engine called SurfaceFlinger. It takes the input of Surface objects from each Windows, then writes the output to the frame buffer. Each Surface is double-buffered. The front buffer is taken by SurfaceFlinger for composition, while the back buffer is used to perform drawing. Once the drawing is completed, Android does a page-flipping by changing the offset value on Y axis of the frame buffer to switch them. During the initialization of EGLDisplaySurface, if the virtual resolution on the Y axis of the frame buffer is found out to be bigger than the real resolution of the Y axis, then the frame buffer is used directly for double-buffering. Otherwise, the back buffer is simply copied to the front buffer, which causes delay in buffer-swapping. Thus double-buffering would better to be supported by the frame buffer driver for the sake of performance.

Android 使用SurfaceFlinger作为屏幕合成引擎。它管理来自各个窗口的Surface objects,然后将其写入到framebuffer去。每一个Surface 都是双缓冲的。SurfaceFlinger使用前buffer来合成,后buffer来绘制。一旦绘制完成,Android通过页翻转操作,交换Y轴坐标的偏移量,选择不同buffer。在EGL显示服务初始化时,如果虚拟Y轴分辨率大于实际Y轴分辨率,说明framebuffer可以直接使用双缓冲。否则,后buffer要复制到前buffer,这样会导致页交换延迟。为了提高系统性能,Framebuffer驱动最好提供双缓冲机制。

Double-buffering is supported in the goldfish frame buffer driver, which is used by the QEMU-based emulator shipped with Android SDK. I happen to be working on porting Android to an ARM RealView board. The board has PL111 PrimeCell Color LCD Controller, which is AMBA compliant SoC peripheral. By comparing the source code of goldfish frame buffer driver (from Android cupcake branch source tree) and that of the ARM LCD controller frame buffer driver (from 2.6.27 vanilla kernel), one can easily figure out what should be patched in the ARM LCD frame buffer driver (kernel/drivers/video/amba-clcd.c) to support double-buffering. 

Goldfish的Framebuffer驱动中提供了双缓冲机制,用于AndroidSDK中基于QEMU的模拟器。我当时在为一块ARM RealView移植板子Android,这板子使用兼容AMBA外设标准的 PL111 PrimeCell Color LCD控制器。通过对比goldfish的Framebuffer驱动(来自Android cupcake分支)和ARM LCD控制器Framebuffer驱动(来自2.6.27 vanilla内核),可以很简单得出应当给ARM LCD Framebuffer驱动(kernel/drivers/video/amba-clcd.c)添加哪些补丁以支持双缓冲。

Firstly, in the initialization of the driver, change to “fb->fb.fix.ypanstep = 1;” and “fb->fb.var.yres_virtual = fb->panel->mode.yres * 2;” Then in the declaration of “clcdfb_ops”, add this line of code “.fb_pan_display = clcdfb_pan_display,”. Also define this new function “clcdfb_pan_display” by putting an invocation to the function “clcdfb_set_start”. In the definition of the function “clcdfb_check” in kernel/include/linux/amba/clcd.h, change from “var->yres_virtual = var->yres = (var->yres + 1) & ~1;” to “var->yres = (var->yres + 1) & ~1; var->yres_virtual = var->yres * 2;” since the checking happens to reset the value of yres_virtual to that of yres. One more step required is to increase the frame size to needed. In kernel/arch/arm/mach-realview/core.c, change from “static unsigned long framesize = SZ_1M;” to “static unsigned long framesize = 0×12C000;”. 0×12C000 is the value of 640 * 480 * 2 * 2 (VGA, 16bpp and double-buffer), because VGA is the largest display resolution that is provided by PL111 and can still support double-buffering due to the limited display memory.

(1)首先,驱动初始化时,修改“fb->fb.fix.ypanstep = 1;” 和 “fb->fb.var.yres_virtual = fb->panel->mode.yres * 2;”。
(2)然后在“clcdfb_ops”声明中添加如下代码“.fb_pan_display = clcdfb_pan_display,”。
(3)定义一个新的函数“clcdfb_pan_display”,调用函数 “clcdfb_set_start”。
(4)在头文件in kernel/include/linux/amba/clcd.h的函数定义“clcdfb_check”中,将“var->yres_virtual = var->yres = (var->yres + 1) & ~1;” 改为“var->yres = (var->yres + 1) & ~1; var->yres_virtual = var->yres * 2;” 防止检查时将yres_virtual用yres替换。
(5)下一步需要增大Framebuffer的空间,内核/arch/arm/mach-realview/core.c中,将“static unsigned long framesize = SZ_1M;”改为“static unsigned long framesize = 0×12C000;”。0×12C000 是 640 * 480 * 2 * 2 (VGA, 16bpp and double-buffer)的值,因为VGA就是PL111提供的最大分辨率,因此这些空间足够双缓冲使用。


How page-flipping works
页交换技术是如何工作的

- In frameworks/base/libs/ui/EGLDisplaySurface.cpp, function “EGLDisplaySurface::swapBuffers()” does the real work by calling ioctl of FBIOPUT_VSCREENINFO
- In “kernel/drivers/video/fbmem.c”, function “fb_ioctl” handles over the work in “case FBIOPUT_VSCREENINFO:”, then “fb_set_var” is invoked. In that function, both “fbops->fb_set_par” and “fb_pan_display” are invoked. Similarly, in “fb_pan_display”, “fbops->fb_pan_display” is also invoked. “fbops->fb_set_par” and “fbops->fb_pan_display” mentioned above are provided by the amba clcd frame buffer driver in kernel/drivers/video/amba-clcd.c.
- According to the conventions, “fb_set_par” implementation in the frame buffer driver is supposed to react to possible changes of screen orientation and resolution. And “fb_pan_display” is supposed to re-map the frame buffer as the offset changes in X or Y axis. In this way, page-flipping is properly handled.

-在 frameworks/base/libs/ui/EGLDisplaySurface.cpp中,“EGLDisplaySurface::swapBuffers()”通过调用FBIOPUT_VSCREENINFO这个ioctl指令工作。
-在“kernel/drivers/video/fbmem.c”中,“fb_ioctl”函数处理“case FBIOPUT_VSCREENINFO:”,调用“fb_set_var”,再调用“fbops->fb_set_par” 和 “fb_pan_display”,同样的,fb_pan_display又调用bops->fb_pan_display。前面提到的“fbops->fb_set_par” 和 “fbops->fb_pan_display”这两个函数位于AMBA CLCD Framebuffer驱动(/drivers/video/amba-clcd.c)。
-根据约定,“fb_set_par”在Framebuffer驱动中用于改变可变参数,如分辨率或屏幕方向等。“fb_pan_display” 用于重新改变X、Y轴后的重新映射,至此页交换可以正常工作。


Problem of “clcdfb_set_par”

In “clcdfb_set_par” of the current amba clcd implementation, it turns off the power and clock source of the frame buffer, sets clocks and the buffer base, then it turns on the frame buffer. This is not applied to Android because the page-flipping gonna invoke FBIOPUT_VSCREENINFO ioctl so often that “clcdfb_set_par” is going to be invoked a lot as well. Then the whole system is not operatable at all as a result. It’s obviously that at least the implementation of “clcdfb_set_par” is not written in a way that adheres to the conventions. Or writting frame buffer implementation in this case still requires keeping the usage of Android in mind.

The solution to that is to cache the frame buffer settings. Each time “clcd_set_par” is invoked, the new frame buffer settings are compared with the cached ones. If the screen orientation, resolution and other changes that will require reset of clock don’t happen, then simply skip any operations; otherwise, the new frame buffer settings are cached and the clock is reseted.

“clcdfb_set_par”的问题
Amba clcd驱动的“clcdfb_set_par”函数会关闭Framebuffer的电源和时钟,重设时钟和缓冲基址,然后再打开Framebuffer。这一点并不适用与Android的页交换技术,因为它会频繁调用FBIOPUT_VSCREENINFO控制命令,“clcdfb_set_par”也会多次调用。这样整个系统工作就不正常了。明显“clcdfb_set_par”不应当这样写,当写Framebuffer驱动时应当对Android的调用方式做到心中有数。

解决方法就是保存显示设置。当调用“clcd_set_par”时,将新的显示设置和以前的进行比较,如果不是改变方向、分辨率和一些需要重新复位时钟的变化,则跳过复位操作,否则保存显示参数,复位时钟。

The actual patch

The patch to the said files above is illustrated below:

 001.diff -Naur kernel-2.6.27.orig/arch/arm/mach-realview/core.c kernel-2.6.27/arch/arm/mach-realview/core.c 
002.--- kernel-2.6.27.orig/arch/arm/mach-realview/core.c    2009-04-29 16:16:29.000000000 +0200 
003.+++ kernel-2.6.27/arch/arm/mach-realview/core.c 2009-04-29 16:12:26.000000000 +0200 
004.@@ -358,7 +358,8 @@ 
005.writel(val, sys_clcd); 
006.} 
007. 
 
008.-static unsigned long framesize = SZ_1M; 
009.+/* 640*480*2*2 (VGA, 16bpp and double-buffer) required by Android */ 
010.+static unsigned long framesize = 0x12C000; 
011. 
 
012.static int realview_clcd_setup(struct clcd_fb *fb) 
013.{ 
014.diff -Naur kernel-2.6.27.orig/drivers/video/amba-clcd.c kernel-2.6.27/drivers/video/amba-clcd.c 
015.--- kernel-2.6.27.orig/drivers/video/amba-clcd.c    2009-04-29 16:16:58.000000000 +0200 
016.+++ kernel-2.6.27/drivers/video/amba-clcd.c 2009-04-29 16:13:16.000000000 +0200 
017.@@ -194,6 +194,37 @@ 
018.return ret; 
019.} 
020. 
 
021.+struct fb_var_screeninfo cached_fb_var; 
022.+int is_fb_var_cached = 0; 
023.+ 
024.+static int clcdfb_is_fb_changed(struct clcd_fb *fb) 
025.+{ 
026.+    if (!is_fb_var_cached || 
027.+        fb->fb.var.xres != cached_fb_var.xres || 
028.+        fb->fb.var.yres != cached_fb_var.yres || 
029.+        fb->fb.var.xres_virtual != cached_fb_var.xres_virtual || 
030.+        fb->fb.var.yres_virtual != cached_fb_var.yres_virtual || 
031.+        fb->fb.var.bits_per_pixel != cached_fb_var.bits_per_pixel || 
032.+        fb->fb.var.grayscale != cached_fb_var.grayscale || 
033.+        fb->fb.var.green.length != cached_fb_var.green.length || 
034.+        fb->fb.var.left_margin != cached_fb_var.left_margin || 
035.+        fb->fb.var.right_margin != cached_fb_var.right_margin || 
036.+        fb->fb.var.upper_margin != cached_fb_var.upper_margin || 
037.+        fb->fb.var.lower_margin != cached_fb_var.lower_margin || 
038.+        fb->fb.var.hsync_len != cached_fb_var.hsync_len || 
039.+        fb->fb.var.vsync_len != cached_fb_var.vsync_len || 
040.+        fb->fb.var.sync != cached_fb_var.sync || 
041.+        fb->fb.var.rotate != cached_fb_var.rotate) { 
042.+ 
043.+        cached_fb_var = fb->fb.var; 
044.+        is_fb_var_cached = 1; 
045.+ 
046.+        return 1; 
047.+    } 
048.+    else 
049.+        return 0; 
050.+} 
051.+ 
052.static int clcdfb_set_par(struct fb_info *info) 
053.{ 
054.struct clcd_fb *fb = to_clcd(info); 
055.@@ -207,22 +238,25 @@ 
056.else 
057.fb->fb.fix.visual = FB_VISUAL_TRUECOLOR; 
058. 
 
059.-   fb->board->decode(fb, &regs); 
060.+    if (clcdfb_is_fb_changed(fb)) { 
061.+ 
062.+       fb->board->decode(fb, &regs); 
063. 
 
064.-   clcdfb_disable(fb); 
065.+       clcdfb_disable(fb); 
066. 
 
067.-   writel(regs.tim0, fb->regs + CLCD_TIM0); 
068.-   writel(regs.tim1, fb->regs + CLCD_TIM1); 
069.-   writel(regs.tim2, fb->regs + CLCD_TIM2); 
070.-   writel(regs.tim3, fb->regs + CLCD_TIM3); 
071.+       writel(regs.tim0, fb->regs + CLCD_TIM0); 
072.+       writel(regs.tim1, fb->regs + CLCD_TIM1); 
073.+       writel(regs.tim2, fb->regs + CLCD_TIM2); 
074.+       writel(regs.tim3, fb->regs + CLCD_TIM3); 
075. 
 
076.-   clcdfb_set_start(fb); 
077.+       clcdfb_set_start(fb); 
078. 
 
079.-   clk_set_rate(fb->clk, (1000000000 / regs.pixclock) * 1000); 
080.+       clk_set_rate(fb->clk, (1000000000 / regs.pixclock) * 1000); 
081. 
 
082.-   fb->clcd_cntl = regs.cntl; 
083.+       fb->clcd_cntl = regs.cntl; 
084. 
 
085.-   clcdfb_enable(fb, regs.cntl); 
086.+       clcdfb_enable(fb, regs.cntl); 
087.+    } 
088. 
 
089.#ifdef DEBUG 
090.printk(KERN_INFO "CLCD: Registers set to\n" 
091.@@ -289,6 +323,17 @@ 
092.return regno > 255; 
093.} 
094. 
 
095.+static int clcdfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) 
096.+{ 
097.+    struct clcd_fb *fb = NULL; 
098.+ 
099.+    info->var = *var; 
100.+   fb = to_clcd(info); 
101.+   clcdfb_set_start(fb); 
102.+ 
103.+   return 0; 
104.+} 
105.+ 
106./* 
107.*  Blank the screen if blank_mode != 0, else unblank. If blank == NULL 
108.*  then the caller blanks by setting the CLUT (Color Look Up Table) to all 
109.@@ -332,6 +377,7 @@ 
110..fb_check_var   = clcdfb_check_var, 
111..fb_set_par = clcdfb_set_par, 
112..fb_setcolreg   = clcdfb_setcolreg, 
113.+   .fb_pan_display = clcdfb_pan_display, 
114..fb_blank   = clcdfb_blank, 
115..fb_fillrect    = cfb_fillrect, 
116..fb_copyarea    = cfb_copyarea, 
117.@@ -367,14 +413,14 @@ 
118.fb->fb.fix.type      = FB_TYPE_PACKED_PIXELS; 
119.fb->fb.fix.type_aux  = 0; 
120.fb->fb.fix.xpanstep  = 0; 
121.-   fb->fb.fix.ypanstep  = 0; 
122.+   fb->fb.fix.ypanstep  = 1; 
123.fb->fb.fix.ywrapstep = 0; 
124.fb->fb.fix.accel = FB_ACCEL_NONE; 
125.fb->fb.var.xres      = fb->panel->mode.xres; 
126.fb->fb.var.yres      = fb->panel->mode.yres; 
127.fb->fb.var.xres_virtual  = fb->panel->mode.xres; 
128.-   fb->fb.var.yres_virtual  = fb->panel->mode.yres; 
129.+   fb->fb.var.yres_virtual  = fb->panel->mode.yres * 2; 
130.fb->fb.var.bits_per_pixel = fb->panel->bpp; 
131.fb->fb.var.grayscale = fb->panel->grayscale; 
132.fb->fb.var.pixclock  = fb->panel->mode.pixclock; 
133.diff -Naur kernel-2.6.27.orig/include/linux/amba/clcd.h kernel-2.6.27/include/linux/amba/clcd.h 
134.--- kernel-2.6.27.orig/include/linux/amba/clcd.h    2009-04-29 16:16:20.000000000 +0200 
135.+++ kernel-2.6.27/include/linux/amba/clcd.h 2009-04-29 16:12:53.000000000 +0200 
136.@@ -232,7 +232,9 @@ 
137.static inline int clcdfb_check(struct clcd_fb *fb, struct fb_var_screeninfo *var) 
138.{ 
139.var->xres_virtual = var->xres = (var->xres + 15) & ~15; 
140.-   var->yres_virtual = var->yres = (var->yres + 1) & ~1; 
141.+   //var->yres_virtual = var->yres = (var->yres + 1) & ~1; 
142.+   var->yres = (var->yres + 1) & ~1; 
143.+   var->yres_virtual = var->yres * 2; 
144. 
 
145.#define CHECK(e,l,h) (var->e < l || var->e > h) 
146.if (CHECK(right_margin, (5+1), 256) ||  /* back porch 
 

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP