代码的深入讲解和调试请参看视频:
Linux kernel Hacker, 从零构建自己的内核
在前几节,我们使用窗体图层叠加技术,解决了窗体,例如鼠标移动时,破坏其他窗体界面的问题,但以此同时,也引入了新的问题。当鼠标移动时,内核会将所有窗口重新绘制,如果当前系统打开的窗口很多,假设有几十上百个,那么鼠标动一下,就重新绘制上百个窗口,这对cpu资源的消耗就会非常大,整个系统的性能就会被拖垮,由此,我们需要优化内核对窗口更新重绘的设计,降低不必要的损耗。
我们现在的鼠标,占据的窗口大小是1616, 也就是256个像素,按照现在内核的重绘机制,只要鼠标稍微移动一下,就需要对整个画面进行刷新,也就是重新绘制320200 = 64000 个像素点,其实只要重新绘制鼠标移动的相关区域就可以了,例如,鼠标从原来坐标点(0,0),移动到坐标点(1,1), 那么内核只要重绘区域[(0,0), (16,16)] 和【(1,1), (17,17)] 即可。也就是只需要绘制的像素是256*2=512,这只要64 000 个像素的百分之0.8, 也就是说,只要我们稍微更改一下绘制机制,整个系统的性能能实现上千倍的提升!
由此,我们添加一个新的绘制函数:
void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1) {
int h, bx, by, vx, vy;
unsigned char *buf, c, *vram = ctl->vram;
struct SHEET *sht;
for (h = 0; h <= ctl->top; h++) {
sht = ctl->sheets[h];
buf = sht->buf;
for (by = 0; by < sht->bysize; by++) {
vy = sht->vy0 + by;
for (bx = 0; bx < sht->bxsize; bx++) {
vx = sht->vx0 + bx;
if (vx0 <= vx && vx < vx1 && vy0 <= vy && vy < vy1) {
c = buf[by * sht->bxsize + bx];
if (c != sht->col_inv) {
vram[vy * ctl->xsize + vx] = c;
}
}
}
}
}
}
这个函数功能就只对每个窗体绘制局部区域,该区域的左上角是(vx0,vy0),右下角是(vx1, vy1).
接着我们改进一下原来的sheet_silde函数,这个函数意义在于窗口移动后重新绘制,现在我们把它改成刷新窗口移动前后所占据的两个区域既可:
void sheet_slide(struct SHTCTL *ctl, struct SHEET *sht, int vx0, int vy0) {
int old_vx0 = sht->vx0, old_vy0 = sht->vy0;
sht->vx0 = vx0;
sht->vy0 = vy0;
if (sht->height >= 0) {
sheet_refreshsub(ctl, old_vx0, old_vy0, old_vx0 + sht->bxsize, old_vy0 + sht->bysize);
sheet_refreshsub(ctl, vx0, vy0, vx0+sht->bxsize, vy0+sht->bysize);
}
}
第一次调用sheet_refreshsub,为的是刷新窗体移动前的区域,第二次调用sheet_refreshsub,是根据窗体移动后的坐标,重新绘制窗体。
原来的sheet_refresh函数也可以根据局部刷新的原理重新实现以下:
int sheet_refresh(struct SHTCTL *ctl, struct SHEET *sht, int bx0, int by0, int bx1, int by1) {
if (sht->height >= 0) {
sheet_refreshsub(ctl, sht->vx0 + bx0, sht->vy0 + by0, sht->vx0 + bx1,
sht->vy0 + by1);
}
return 0;
}
还有一处需要改进的是文字的显示,原理我们显示文字时,同样是需要把所有窗口重新绘制一次,二是我们的文章只能绘制在桌面背景上。现在我们有了图层的概念,因此我们可以把文字显示机制改进一下,改进一是只对文章所占据的区域进行刷新,二是文字可以绘制到给定的图层上,这样文章就可以绘制到指定的窗体,而不再只限制于桌面,代码如下:
void showString(struct SHTCTL *shtctl ,struct SHEET *sht, int x, int y, char color, unsigned char *s ) {
int begin = x;
for (; *s != 0x00; s++) {
showFont8(sht->buf, sht->bxsize, x, y,color, systemFont+ *s * 16);
x += 8;
}
sheet_refresh(shtctl, sht, begin, y, x , y + 16);
}
输入参数中的sht对应要绘制字符串的窗口图层,调用的sheet_refresh目的就是仅仅更新文字所占据的区域。
有了上面的改进后,虽然我们的系统在外在表现上,没有明显的变化,但实际上,性能已经有了上千倍的提升。