一种简单的帧缓冲区管理机制和C语言代码实现

对于有些场景下,我们需要使用帧缓冲区来显示图像,实际上我们可以用一个循环缓冲区标识当前可用的帧,如果多个帧缓冲区的地址是不连续的,那么还需要一个链表结构来保存下一个帧缓冲区的地址。那有没有什么更简单的实现呢?

1 使用场景

比如显示一个摄像头的图像,它是一帧一帧显示的。假设有三个缓冲区framebuffer[3][WIDTH*HEIGHT*BPP],将缓冲区填充完待显示的图像后,交由LCD控制器来显示,在显示完后有一个中断回调函数指示图像成功绘制到LCD中,此时就可以标记这缓冲区为未使用。

在这个过程中间,可能LCD还没有绘制完上一帧,下一帧摄像头图像就来了,所以现在就将新的摄像头帧保存在下一个帧缓冲区中,显示下一帧的图像。这样显示出来的摄像头图像才是流畅的。有时摄像头的图像来得很快,3个缓冲区就用满了,就需要等待中断回调函数中释放缓冲区。

所以下面就来介绍一种简单的帧缓冲区管理机制。

2 原理

前面有提到,如果帧缓冲区的地址不是连续的,就还需要记录各个帧缓冲区的地址,类似一个链表的操作。对于没有使用的帧缓冲区来说,我们就可以用帧缓冲区自身的内存来保存这个地址,然后用一个指针变量s_fbList来指向最新的缓冲区。每次有新的帧缓冲区加入时,将新的帧缓冲区的前4个字节赋值为前一个帧缓冲区的地址。整个数据结构与栈有些类似,实现思路如下所示:
在这里插入图片描述
所以这就要求了每个帧缓冲区的大小必须大于4字节(在32位机器上),也就是能保存一个指针的地址。

3 C语言实现

代码如下所示,可以看到代码十分精简,就两个函数:一个获取帧缓冲区的函数APP_GetFrameBuffer和一个增加帧缓冲区的函数APP_GetFrameBuffer

static void *volatile s_fbList = NULL;
static void *APP_GetFrameBuffer(void)
{
    void *fb;

    fb = s_fbList;

    if (NULL != fb)
    {
        s_fbList = *(void **)fb;
    }

    return fb;
}

static void APP_PutFrameBuffer(void *fb)
{
    *(void **)fb = s_fbList;
    s_fbList     = fb;
}

我们只要在程序初始化的时候初始化几个帧缓冲区,然后在使用时调用APP_GetFrameBuffer获取一个帧缓冲区,最后在LCD的中断回调函数中调用APP_PutFrameBuffer释放刚刚获取的帧缓冲区即可。

注意这里二维指针的使用,比如在APP_PutFrameBuffer中,如果直接将fb = s_fbList的话,似乎和*(void **)fb = s_fbList一样都是改变fb地址处的值。但这样实际上是把函数创建的临时指针变量指向的地址改变了。所以如果要改指针所指向地址里面的值的话,需要先强制转化为二维指针,再访问里面的内容。

以这个思路的话,假设LCD显示的速度足够快,那么可能在初始化时,我们最早调用APP_PutFrameBuffer加入的帧缓冲区0可能永远都用不上。

4 总结

平时写代码的过程中经常会遇到一些有趣的数据结构,比如这个帧缓冲区的机制,我看到就这几行代码很是好奇里面的原理。当然,我不去理解里面的原理也可以,因为这段代码可以正常运行,这也不会影响我项目的进度。但如果你花时间来理解里面的原理,掌握不同的数据结构,以后在你写代码的过程中也会有更多的选择和思路。

猜你喜欢

转载自blog.csdn.net/tilblackout/article/details/131833905