记一个framebuffer显示混乱的低级错误
由于framebuffer的基础知识不扎实,这个任务上我多卡了两天,差点把我搞死,于此记录为后鉴。
打算用awtk做一个多进程项目,计划把framebuffer的内容通过websocket输出到浏览器上去显示画面, 架构大概为:
browser部署项目<---websocket<------framebuffer输出程序
用于输出framebuffer的项目在公司的嵌入式设备平台上显示良好:
但是换到ubuntu服务器上部署时,在浏览器上显示,却变成了这样:
检查framebuffer输出程序内部代码,mock数据给跟fb打交道的中间bitmap变量,然后再推流:
void update_g_bitmap_backgroud() {
rect_t r = rect_init(0, 0, g_bitmap->w, g_bitmap->h);
char *shared_data = LibFrameBufferGet(USE_FB_ID)->fbdata;
bitmap_t temp_bmp;
bitmap_init(&temp_bmp, 800, 480, BITMAP_FMT_BGRA8888, shared_data);
// ******* add
rect_t temp_r = rect_init(0, 0, temp_bmp.w / 3, temp_bmp.h);
image_fill(&temp_bmp, &temp_r, color_init(0, 0, 255 , 255));
temp_r = rect_init(temp_bmp.w / 3, 0, temp_bmp.w / 3, temp_bmp.h);
image_fill(&temp_bmp, &temp_r, color_init(0, 255, 0 , 255));
temp_r = rect_init(temp_bmp.w * 2 / 3, 0, temp_bmp.w / 3, temp_bmp.h);
image_fill(&temp_bmp, &temp_r, color_init(255, 0, 0, 255));
// ********
helper_bitmap_bgra8888_to_rgba8888(g_bitmap, &temp_bmp);
bitmap_deinit(&temp_bmp);
}
浏览器上正常显示:
那看来是获取fb这段的逻辑或者输出到fb的程序出了问题,但是检查一天,并未发现明显的错误,这时候通过awtk-linux-fb的输出日志注意到一个现象,我之前用的测试机器的line_length是3200,像素显示位数32 / 8 = 4 pixel, 正好对应机器显示分辨率xres= 3200/ 4 = 800:
app_root=./res
devices_load : path = file:///home/zhangdalin/AWStudioProjects/kp25sweb/process/Awtk_GamiWebDisplayStream/config/devices.json
devices[0]: path = /dev/fb0, type = fb
devices[1]: path = /dev/dri/card0, type = drm
devices[2]: path = /dev/input/event1, type = input
devices[3]: path = /dev/input/event2, type = mouse
fb_info_t: /dev/fb0
xres=800 yres=480
xres_virtual=800 yres_virtual=480
bits_per_pixel=32 line_length=3200
fb_info_t: red(16 8) green(8 8) blue(0 8)
xpanstep=1 ywrapstep=0
fb_size=768000 fb_total_size=1536000 fb_nr=2 smem_len=1536000
fb_open clear
fb_open ok
run in vmware and fix FBIOPAN_DISPLAY block issue
=========fb_number=2
ratio=1.000000 800 480
ratio=1.000000 800 480
而在ubuntu机器上,同样像素字节数4位,line_length=4704对应虚拟分辨率xres_virtual=4704 / 4 = 1176。
app_root=./res
devices_load : path = file:///home/zhangdalin/AWStudioProjects/kp25sweb/process/Awtk_GamiWebDisplayStream/config/devices.json
devices[0]: path = /dev/fb0, type = fb
devices[1]: path = /dev/dri/card0, type = drm
devices[2]: path = /dev/input/event1, type = input
devices[3]: path = /dev/input/event2, type = mouse
fb_info_t: /dev/fb0
xres=800 yres=600
xres_virtual=1176 yres_virtual=885
bits_per_pixel=32 line_length=4704
fb_info_t: red(16 8) green(8 8) blue(0 8)
xpanstep=1 ywrapstep=0
fb_size=2257920 fb_total_size=4163040 fb_nr=2 smem_len=4163040
fb_open clear
fb_open ok
run in vmware and fix FBIOPAN_DISPLAY block issue
=========fb_number=2
ratio=1.000000 800 600
ratio=1.000000 800 600
这时候已经接近线索了,可惜framebuffer基础没怎么过,以为是分辨率不一样的问题,还用fbset去改,结果当然是无功而返,实际上分辨率设多少都只是放缩画布的大小,只要在项目的设置里面设定了分辨率800x480, 那它就会以(0,0)起点绘制一个800x480的画面。
比如fbset设置分辨率1024x768的情况:
fbset搞了半天,始终没有找到问题的解决方法,干脆想把ubuntu虚拟机的fb设置改成和机器一致,但fb设置属于linux驱动范畴,已经超出了我的能力,眼看这个任务卡到明天毫无头绪,就这么卡死了?
现在想起来我是忽略了这几个点:
-
framebuffer的读取方式是从左到右,从上到下的
-
framebuffer的参数分variable和fix两类,fbset能修改的是xres, yres, xres_virtual这类的,但是line_length和smem_len都属于fix类参数,fbset改不了
-
awtk中的bitmap_init函数是直接引入外来的一维数组指针的,读取方式和fb一样,但是我想当然的以为是引用整个fb框内从(0,0)左上角为起点的800x480的区域。
-
framebuffer一行的长度取决于line_length, 而我当时又想当然的认为一行长度是fbset设置的分辨率宽度*像素位数,基于此,我的framebuffer接口mmap是这么写的:
size_t screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8; fb->fbdata = (unsigned char *)mmap(NULL, screensize, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0); if (fb->fbdata == MAP_FAILED) { perror("Error mapping framebuffer to memory"); exit(EXIT_FAILURE); }
但是后面看awtk-linux-fb的源码,才发现smem_len才会把整个framebuffer都包括进去,于是我改成:
fb->fbdata = (unsigned char *)mmap(NULL, finfo.smem_len, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);
推流定时器函数逻辑改成:
void update_g_bitmap_background() {
rect_t src_r = rect_init(0, 0, g_bitmap->w, g_bitmap->h);
LibFrameBuffer *libframebuffer = LibFrameBufferGet(USE_FB_ID);
char *shared_data = libframebuffer->fbdata;
int line_length = libframebuffer->fixinfo.line_length / libframebuffer->bpp;
int height = libframebuffer->height;
bitmap_t temp_bmp;
bitmap_init(&temp_bmp, line_length, height, BITMAP_FMT_RGBA8888, shared_data);
...
终于正常显示。
在这个ub虚拟机的fb是个长为1176x885的一维数组,而且每1176字节为一行长度,而我只取了800x480的部分放到800x480的位图,在这个位图上,一行只显示800个像素,剩下的376个像素只能到800x480位图第二行去显示了,这样800x480位图两行才能存放1176x885位图的一行内容,后面的行以此类推,这就解释了为什么出来的800x480位图上出来的图像像是拧毛巾(不知道该怎么比喻。。。。)一样。
嵌入式做业务,底层基础一定要过关啊!
ADD:
ubuntu 20.04中:
- Ctrl + Alt + F2:GUI模式
- Ctrl + Alt + F6:命令行模式