最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

做嵌入式開發(fā),Linux應(yīng)用好還是驅(qū)動(dòng)好?

2022-06-08 20:40 作者:大方老師單片機(jī)課堂  | 我要投稿

做嵌入式開發(fā),Linux應(yīng)用好還是驅(qū)動(dòng)好?

一、為何需 double buffer?

single buffer會(huì)導(dǎo)致:

文章下方附學(xué)習(xí)資源,自助領(lǐng)取。屏幕撕(tearing),即在屏幕上同時(shí)看到多幀數(shù)據(jù)拼接在一起。


點(diǎn)擊查看大圖

single buffer為何會(huì)造成撕裂:

refresh rate frame rate不一致。

refresh rate表示的屏幕每秒能更新多少次顯示,例 30hz / 60hz。


點(diǎn)擊查看大圖

frame rate表示的 lcd controller / gpu每秒能繪制多少幀數(shù)據(jù),例 30fps / 60fps。


點(diǎn)擊查看大圖

LCD controller / gpu屏幕協(xié)作完成一幀圖像的顯示:


點(diǎn)擊查看大圖

single buffer的場(chǎng)景下LCD user LCD controller / gpu總是在共用同一個(gè) framebuffer,且沒有同步機(jī)制。

LCD user是寫者,LCD controller / gpu是讀者。

由于存在競(jìng)爭(zhēng)關(guān)系且讀寫沒有同步機(jī)制,framebuffer里必須會(huì)發(fā)生同時(shí)存frame N frame N-1的數(shù)據(jù),此時(shí) LCD framebuffer的數(shù)據(jù)顯示出來(lái)時(shí),就會(huì)看到撕裂的效果:


點(diǎn)擊查看大圖

可以通過(guò) double buffer+vsync解決撕裂的問(wèn)題。

double buffer,顧名思義,就是 2個(gè) framebuffer,其工作邏輯如下:

·LCD controller : draw fb0 to screen

·LCD user : write data to fb1

·LCD controller : draw fb1 to screen

·LCD user : write data to fb0

·環(huán)...

vsync機(jī)制則用于確保一幀圖像能不被打斷地顯示在屏幕。

如何支 double buffer?

需要驅(qū)動(dòng)和應(yīng)用互相配合:


二、編寫支 double buffer fbdev驅(qū)動(dòng)

fbdev框圖:


先梳理一下思路:

讓驅(qū)動(dòng)支 double buffer需要 3件事。

1.請(qǐng)2 x buffer

size = (2 * width * height);

fbi->screen_base = dma_alloc_wc(sfb->dev, size, &map_dma, GFP_KERNEL);

2. buffer相關(guān)的信息保 struct fb_info-> struct fb_var_screeninfo。

struct fb_var_screeninfo {

__u32 xres; /* visible resolution */

__u32 yres;

__u32 xres_virtual; /* virtual resolution */

__u32 yres_virtual;

__u32 xoffset; /* offset from virtual to visible */

__u32 yoffset; /* resolution */

...

}


xres yres是真實(shí) LCD分辨率的寬和長(zhǎng);

xres_virtual yres_virtual是顯存區(qū)域的寬和長(zhǎng);

xoffset yoffset用于指定當(dāng)前使用哪一個(gè) Buffer進(jìn)行繪制。使 Buffer0時(shí)xoffset = 0,yoffset=0;使 Buffer1時(shí),xoffset = 0, yoffset = yres * 1;

3.支持切 buffer,具體的就是實(shí)現(xiàn) ioctlFBIOPAN_DISPLAY。

pan的本意是平移,可以想象成顯存上方有一個(gè)取景框,平移取景框可以看到不同的顯示內(nèi)容。

實(shí)例分析goldfishfb.c

goldfishfb.c是虛擬硬 goldfish fbdev驅(qū)動(dòng),我們可以參考這個(gè)文件,學(xué)習(xí)如何實(shí)現(xiàn) double buffer。

1. 2 x buffer

int goldfish_fb_probe()

{

...

framesize = width * height * 2 * 2;

fb->fb.screen_base = (char __force __iomem *)dma_alloc_coherent(&pdev->dev, framesize, &fbpaddr, GFP_KERNEL);

}

2.設(shè) fb_var_screeninfo

int goldfish_fb_probe()

{

...

fb->fb.var.xres = width;

fb->fb.var.yres = height;

fb->fb.var.xres_virtual = width;

fb->fb.var.yres_virtual = height * 2;

}

3.實(shí)現(xiàn) ioctl / FBIOPAN_DISPLAY

static struct fb_ops goldfish_fb_ops = {

...

.fb_pan_display = goldfish_fb_pan_display,

};

int goldfish_fb_pan_display()

{

...

//將新的顯存地址告 lcd controller

writel(fb->fb.fix.smem_start + fb->fb.var.xres * 2 * var->yoffset,

fb->reg_base + FB_SET_BASE);

// LCD controller vsync信號(hào)

wait_event_timeout(fb->wait,fb->base_update_count != base_update_count, HZ / 15);

}

當(dāng)LCD controller將一幀圖像完整地顯示 LCD上后,就會(huì)產(chǎn)生一個(gè)中斷,在中斷里就會(huì)執(zhí)行喚醒睡眠 fb_pan_display里的進(jìn)程。

如果你想多了解一些,可以閱 DRM框架里 fbdev兼容代碼,此代碼也是支 double buffer的:

·linux/drivers/gpu/drm/*/*_drm_fbdev.c

·linux/drivers/gpu/drm/drm_fb_helper.c

三、編寫支 double buffer fbdev應(yīng)用

驅(qū)動(dòng)支 double buffer后,還得在應(yīng)用程序里將其使用起來(lái)。

先梳理一下思路:

.檢查是否支 double buffer;

.使 double bufferFBIOPUT_VSCREENINFO;

. buffer里數(shù)據(jù);

.通知驅(qū)動(dòng)切 bufferFBIOPAN_DISPLAY;

.等待切換完成FBIO_WAITFORVSYNC;

實(shí)例分析show_color.c

static int fd_fb;

static struct fb_fix_screeninfo fix; /* Current fix */

static struct fb_var_screeninfo var; /* Current var */

static int screen_size;

static unsigned char *fb_base;

static unsigned int line_width;

static unsigned int pixel_width;

int main(int argc, char **argv)

{

int i;

int ret;

int buffer_num;

int buf_idx = 1;

char *buf_next;

unsigned int colors[] = {0x00FF0000, 0x0000FF00, 0x000000FF, 0, 0x00FFFFFF}; /* 0x00RRGGBB */

struct timespec time;

...

fd_fb = open("/dev/fb0", O_RDWR);

ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix);

ioctl(fd_fb, FBIOGET_VSCREENINFO, &var);

line_width = var.xres * var.bits_per_pixel / 8;

pixel_width = var.bits_per_pixel / 8;

screen_size = var.xres * var.yres * var.bits_per_pixel / 8;

// 1. buffer個(gè)數(shù)

buffer_num = fix.smem_len / screen_size;

printf("buffer_num = %d\n", buffer_num);

fb_base = (unsigned char *)mmap(NULL , fix.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);

if (fb_base == (unsigned char *)-1) {

printf("can't mmap\n");

return -1;

}

if ((argv[1][0] == 's') || (buffer_num == 1)) {

printf("single buffer:\n");

while (1) {

for (i = 0; i < sizeof(colors)/sizeof(colors[0]); i++) {

lcd_draw_screen(fb_base, colors[i]);

nanosleep(&time, NULL);

}

}

} else {

printf("double buffer:\n");

// 2.使能 buffer

var.yres_virtual = buffer_num * var.yres;

ioctl(fd_fb, FBIOPUT_VSCREENINFO, &var);

while (1) {

for (i = 0; i < sizeof(colors)/sizeof(colors[0]); i++) {

// 3. buffer里的數(shù)據(jù)

buf_next = fb_base + buf_idx * screen_size;

lcd_draw_screen(buf_next, colors[i]);

// 4.通知驅(qū)動(dòng)切 buffer

var.yoffset = buf_idx * var.yres;

ret = ioctl(fd_fb, FBIOPAN_DISPLAY, &var);

if (ret < 0) {

perror("ioctl() / FBIOPAN_DISPLAY");

}

// 5.等待幀同步完成

ret = 0;

ioctl(fd_fb, FBIO_WAITFORVSYNC, &ret);

if (ret < 0) {

perror("ioctl() / FBIO_WAITFORVSYNC");

}

buf_idx = !buf_idx;

nanosleep(&time, NULL);

}

}

}

munmap(fb_base , screen_size);

close(fd_fb);

return 0;

}

運(yùn)行:

$ ./show_color single

buffer_num = 1

single buffer:

$ ./show_color double

buffer_num = 2

double buffer:

該程序會(huì)在屏幕上循環(huán)的顯示不同的顏色。

當(dāng)傳 "single"參數(shù)時(shí),使用 buffer,可見撕裂。

當(dāng)傳 "double"參數(shù)時(shí),使用 buffer,不再撕裂。

代碼不是很復(fù)雜,我就不再詳細(xì)分析了。

如果你想多了解一些,可以閱讀開源軟 SDL-1.2 sdl_fbvideo.c,此代碼也支持 double buffer。

另外,現(xiàn)在越來(lái)越多的顯示設(shè)備走的 DRM框架,該框架自然是支持 buffer的。


做嵌入式開發(fā),Linux應(yīng)用好還是驅(qū)動(dòng)好?的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
拜泉县| 西盟| 达州市| 垣曲县| 巴林左旗| 太仓市| 安龙县| 阿拉善左旗| 镇巴县| 仁寿县| 西盟| 科技| 中卫市| 北川| 格尔木市| 西充县| 平舆县| 建始县| 开阳县| 东乡族自治县| 赣榆县| 隆德县| 孟村| 离岛区| 平乡县| 汾阳市| 顺平县| 栾城县| 遵化市| 吴桥县| 北京市| 迭部县| 新余市| 陈巴尔虎旗| 密山市| 临湘市| 信阳市| 霍山县| 资溪县| 恩施市| 宁陕县|