lurker0 发表于 2009-3-17 11:08:18

(讨论)关于LCD仿真的速度的改进

不成熟的意见 希望和大家讨论

在skyeye_lcd_win32.c里面,一共有三个存储块涉及到LCD framebuffer
1.LCD dma
2.fHDC
3.fscreenDC

LCD仿真程序每个200个tick从LCD dma同步到Win32程序的客户区
一共涉及到两次拷贝
第一次: SkyEyeLCD_UpdateFromSkyEye函数中从dma拷贝到fHDC(内存)
第二次: SkyEyeLCD_DoUpdate函数中从fHDC拷贝到屏幕DC

改进的地方有两个:
1. 可以省去第一步拷贝.提供一个新函数dma直接用新申请的fHDC内存,把这两边内存合二为一.可以减少一次拷贝.
2. 采用"中断:的形式查询LCD更新. GDI的bitblt速度很慢,所以没有必要尽量不要调用.每200tick调用一次还是很频繁的.skyeye内部可以提供一个标志位什么的用于指示LCD framebuffer已经被更新过了. 这样LCD前端在检查到标志位更新之后才做更新.

我现在只看了win32的实现,其他的还没看.我想应该有遗漏的细节,希望大家踊跃拍砖.

AnthonyLee 发表于 2009-3-30 03:08:43

回复 1# lurker0 的帖子

交待一下情况。 :)

1. 第一步中把虚拟的 LCD 缓冲区变换确实可以直接写到屏幕 DC 去,
但假如用 API32 写点的话,其效率比现双缓冲的速度还慢。
要实现你说的合二为一,可能要考虑采用 DX 了。

2. 有考虑过你说的情况(在 xxx_filter_xxx 函数可看出痕迹)... :)
可比较可惜的是当大区域更新时,那种方法比较耗时,
也许需要结合 LCD 各模块的模拟作调整。

LCD 虚拟应该还有很大的改善空间,欢迎加入到团队一起合作。:)

lurker0 发表于 2009-3-30 12:19:28

感谢Anthony的回复

你说的的确是是个问题. 我正在考虑怎么才能优化LCD的模拟.
如果考虑到跨平台,opengl可能是个更好的选择.不过这不是一个大问题.

lurker0 发表于 2009-3-30 17:40:34

想另外请教一下一处代码问题

在函数callback_expose_event(skyeye_lcd_gtk.c)中
case 32:
            gdk_draw_rgb_32_image(widget->window,
                        widget->style->fg_gc,
                        0, rect.y, lcd->width, rect.height,
                        GDK_RGB_DITHER_NORMAL,
                        (guchar*)((u32*)lcd->rgbbuf + rect.y * lcd->virtual_width),
                        lcd->virtual_width * 4);
红色的这一行是不是应该是 (guchar*)((u32*)lcd->rgbbuf + rect.y * lcd->virtual_width * 4),

AnthonyLee 发表于 2009-3-30 23:44:13

回复 4# lurker0 的帖子

lcd->virtual_width 是虚拟屏(部分模拟采用缓冲)的每行像素点数,
RGBA模式下每个点缓冲区描述是一个 32bits,
那么目标行起始缓冲数据是 (u32*)lcd->rgbbuf + rect.y * lcd->virtual_width 或 (guchar*)lcd->rgbbuf + rect.y * lcd->virtual_width * 4,
用(guchar*)(XXX)只是强制转换指针类型以便编译的时候不出现警告信息而已。

lurker0 发表于 2009-3-31 10:05:00

我现在在考虑是否可以把LCD模拟和LCD实现从源代码上分离.
当前考虑的解决方案是把LCD DMA的数据直接写到文件中.这样的话,skyeye这边可以只向文件中写像素,而LCD的显示部分就从skyeye中独立出来.以后如果要实现GUI 接口,类似VirtualBox或者vmware的集成界面,会容易很多.因为不再需要skyeye源代码的支持.    另外skyeye得到的好处是不再需要GTK开发环境的支持,更容易移植.   我相信posix文件接口在几乎所有OS上都有实现.

举个例子:
我们有一个新的后端叫做skyeye_lcd_file类似skyeye_lcd_gtk
在skyeye_lcd.c中直接使用open   read   write(无缓存文件操作函数), 或者直接mmap文件到
虚拟空间. 一个我能想到的好处是如果要截屏之类的功能,就用gimp或者photoshop之类的软件直接打开这个文件就可以了(raw格式).

另外在LCD实现端,可以用实现GTK/GDI/QUERTZ之类的API,实现也很简单
用mmap或者CreateFileMapping映射文件,直接拷贝到屏幕就可以(skyeye当前的实现就是这样子) ;    如果考虑到硬件加速实现,可以实现opengl,   使用glTexGen/glBindTexture 系列绑定为纹理,然后显示在屏幕.   

另外需要一个文件也用mmap的方式映射到skyeye中,其中的内容是DMA格式信息.
typedef struct FBHDR{
   ULONG header_size;   //in unit of bytes
   ULONG version;
   ULONG width;
   ULONG length;
   ULONG depth;
   ULONG format;   
   ULONG IsUpdate;
   ULONG rectangle_x;
   ULONG rectangle_y;
   ULONG rectangle_width;
   ULONG rectangle_length;
}Framebuffer_header;
这个东西有什么好处,在LCD显示部分,可以轮训IsUpdate成员检查FrameBuffer是否更新,而不用每隔多少时间强制跟新一次.因为我们模拟arm指令实际上很慢, 实际处理LCD IO的时间并不是很多.通过轮训的方法可以很大程度上减少CPU时间.

AnthonyLee 发表于 2009-3-31 11:32:17

回复 6# lurker0 的帖子

因为目前 SkyEye 的指令模拟采用轮询而非类似 syscall 方式,
所以对于单个 CPU 的机器来说,运行 SkyEye 的时候几乎占据了大片 CPU 资源,
外部程序响应速度将非常缓慢。

或许能够在 SkyEye 端设置一个标准,
比如 FrameBuffer 更新量达到一定程度或者最后一次更新间隔一定时间后
强制把 CPU 片交给外部程序。

lurker0 发表于 2009-3-31 16:53:39

CPU占有率确实是个很大的问题.我在server上运行skyeye,其他人基本上没法干活了.
我现在在代码中稍微改了一下:
在io_do_cycle中进行计数
每10000次IO释放就usleep(10);
CPU占有率大约为50%,勉强可以接受.

我以前试过每100次做一次释放.结果就是慢的要死.
模拟平台上的linux像是死掉了一样, 害得我以为哪儿改坏了,debug了很长时间.
最后发现是太慢了.:-(

sleep太多,skyeye会很慢.想要快,占有率就会很高.

模拟类的软件一般都有这个问题, 比如模拟Cisco路由器的Dynamips 最早的时候就有CPU 100%问题, 后来采用idlepc机制才稍稍好一点.qemu的策略好像是在模拟 hlt /idle 指令的时候sleep一段时间.后来他们发现window 95模拟的时候还是100%,因为95 没采用hlt 指令.

========

void
io_do_cycle (void * state)
{
    struct device_desc *dev;
    int i;
#if 0
#ifdef DBCT_TEST_SPEED
    state->instr_count++;
#endif//DBCT_TEST_SPEED
#endif
    prescale--;
    static cnt = 0;
    cnt ++;
    if (cnt % 10000 == 0)
    {
       usleep(10);
    }

    if (prescale < 0) {
      prescale = DIVISOR;
      for (i = 0; i < skyeye_config.mach->dev_count; i++) {
            dev = skyeye_config.mach->devices;
            if (dev->update)
                dev->update (dev);
      }
      skyeye_config.mach->mach_io_do_cycle (state);
    }
}

lurker0 发表于 2009-4-5 14:40:40

我来通报一下进展吧

在原始版本上做了一下性能测试,
在s3c2410的测试包上, 大约平均2.5ms会有一次更新LCD操作.

usec:1941 cnt:1 update:0x1
usec:2036 cnt:1 update:0x1
usec:3044 cnt:1 update:0x1
usec:2968 cnt:1 update:0x1
usec:2928 cnt:1 update:0x1
usec:1961 cnt:1 update:0x1
usec:2064 cnt:1 update:0x1
usec:1909 cnt:1 update:0x1
usec:2065 cnt:1 update:0x1
usec:5042 cnt:1 update:0x1
usec:2892 cnt:1 update:0x1
usec:1982 cnt:1 update:0x1
usec:1978 cnt:1 update:0x1
usec:2050 cnt:1 update:0x1
usec:1968 cnt:1 update:0x1
usec:1938 cnt:1 update:0x1
usec:1983 cnt:1 update:0x1
usec:2021 cnt:1 update:0x1
usec:1996 cnt:1 update:0x1
usec:1950 cnt:1 update:0x1
usec:1994 cnt:1 update:0x1
usec:2032 cnt:1 update:0x1
usec:1991 cnt:1 update:0x1
usec:3120 cnt:1 update:0x1
usec:5082 cnt:1 update:0x1
usec:2716 cnt:1 update:0x1
usec:1987 cnt:1 update:0x1
usec:2144 cnt:1 update:0x1
sum:250635 aver:2481 usec

ksh 发表于 2009-4-5 18:01:26

1. 对于硬件上不支持idle或者OS没有用 idle指令的系统,SkyEye如何判断什么时候应该sleep,这个有待研究。如果我们用OS Aware的方式去降低skyeye的cpu使用率, 我们可以用判断在客户端上的linux OS的idle函数是否执行,如果执行,我们就可以通知SkyEye去sleep。

2. 对于LCD刷新的时机,我这样考虑,每一个硬件应该都有一个divisor的值,最小为1,来控制轮询每个硬件的次数。
假设LCD设为10,那就每执行 10条指令轮询一次lcd是否要更新的状态。
uart可以设为50,每执行50条指令来轮询一次lcd是否要更新的状态。
根据外设的速度我们来设置不同的divisor。

lurker0 发表于 2009-4-8 12:15:24

to KSH:
1.用OS Aware是一个能看得见的好方法.不过带来的副作用是模拟的OS和最后的上板的image不一致的.这取决于用户是怎么用skyeye,如果只是用于开发应用系统,则基本上没有影响. 如果是用于调试驱动,学习硬件的目的,可能会带来模拟行为上的不一致. 基于这样的考虑,所以我才在我的本地代码里面采用在io处理函数中sleep的土方法.

2. 这个想法很有创意.这样慢速的设备就不用在每个IO周期都要检查一下模拟硬件的状态.节省不少时间.而且本来各个硬件就是有时钟频率的差异.这样做更符合实际.

lurker0 发表于 2009-4-8 12:30:29

再通报一下进展吧

按照我原来的想法, 用mmap来映射一个文件到虚地址空间当LCD用. 通过这种方法来实现前后端分离.如果实现,以后LCD后端可以不用skyeye的源代码就可以编译.可以用GTK WIN32 SDL任何你想用的GUI技术. skyeye也可以专注于模拟,而不是GUI.

现在的进展是已经把file接口完成了,因为不会写autoconfigure,所以我把Makefile中skyeye_lcd_gtk的地方都替换为skyeye_lcd_file来实现编译. 另外写了一个小的测试程序来测试我的新接口.   现在遇到的问题是通过测试程序,发现LCD更新的很频繁.原来的skyeye程序中设定200ms更新一次,现在是平均2.5ms. 原因就是ksh上面所说的io_cycle中每个io周期都有的硬件update, 而每次update,LCD模拟部分都会从模拟sdram中拷贝像素到目标像素区.
这样达不到我最初减少LCD模拟负荷的设想. 所以我现在正在犹豫要不要继续做下去.

修改后的接口除非是回到定时更新的老路,然后利用opengl绑定为纹理的方法来实现硬件加速.不然不会有速度的提升.原来我以为skyeye中有专门的LCD写像素模拟操作.后来经过分析,发现s3c2410是通过在sdram中指定显存的方法来实现绘图,也就是说绘图就写内存.除非是把绘图和其他写内存操作区分开,不然没法做效能上的优化.

ksh 发表于 2009-4-10 13:32:16

1、恩,有关开发的一些具体小问题,你可以直接发邮件给我(blackfin.kang at gmail.com),这样的话,你的一些改动我将来帮你合并到svn的仓库中。
2、我们现在代码中已经有了一些divisor的设定,你可以尝试在lcd模型中加入divisor,让每个外设刷新的频率不一致。
3、目前绘图就是内存拷贝,把客户操作系统想显示的数据拷贝到模拟的显存的位置,这个优化我还没有什么更好的想法。如果能多做一些优化,我们的LCD模拟显示很多图形界面就会流畅起来。

lurker0 发表于 2009-4-13 10:25:23

to KSH,

你说的divisor的设定是不是在最新版才有的.
我用的是skyeye-1.2.7_rc1,好像没找到对应的地方.

lurker0 发表于 2009-4-13 10:42:51

哦   不好意思
找到地方了
页: [1] 2
查看完整版本: (讨论)关于LCD仿真的速度的改进