跳转到主要内容

通过部分帧缓冲降低内存使用率

This section explains, by exemplifying with a clock application, how to configure and use Partial Frame Buffers with a display with GRAM, to lower memory requirements at the expense of some performance.

下面是在STM32L4R9Discovery评估套件上运行的应用的视频

完整帧缓冲内存

帧缓冲区通常是一个大型内存数组,其存储空间足以容纳显示屏上的所有可用像素数据。 如果在分辨率为480 x 272的24位显示屏上运行,则完整大小的帧缓冲区可容纳480 x 272 x 3字节 = 391,680字节。

一些应用可能有2(“双重缓冲”)甚至3个帧缓冲区。 在这种情况下,要求的总存储空间将是783,360和1,175,040字节。

在绘制UI的任何部分时,TouchGFX向帧缓冲区写入像素值,在所有绘制操作完成后,帧缓冲区被传输到显示屏。 通常会将整个帧缓冲区传输到显示屏,即使只更新了UI的一部分。 在传输前,通常可以将帧缓冲分成许多小块进行更新。

更新1、更新2、更新3、……、更新N,传输至显示屏

在某些情况下,特别是在没有外部RAM的低成本解决方案中,帧缓冲区必须足够小,使内部RAM能满足帧缓冲和应用其余部分对内存的需求。 部分帧缓冲区在这种情况下十分有用。

部分帧缓冲内存

部分帧缓冲区使TouchGFX应用能够在几个小于完整大小的帧缓冲区之上运行。 帧缓冲区的数量和大小是可配置的。 此技术可大幅降低应用的内存空间要求,但也带来了一些限制:

  • 部分帧缓冲区只能在具有内置存储器的显示屏上工作。 这些显示屏通常是DSI显示屏或具有并行总线连接(DBI A/B型,8080/6800)或SPI总线连接的显示屏。
  • 复杂应用可能发生撕裂。

完全帧缓冲区能存储显示屏上每一个像素的显示数据,与此不同,部分帧缓冲区通常只覆盖部分区域。 在本文使用的时钟示例中,使用了三个大小均为11,700字节的帧缓冲区。 这样帧缓冲区总共占用存储器35,100字节的空间。

当应用需要更新UI的某一部分时,TouchGFX将选择配置的部分帧缓冲区中的一个,在该部分帧缓冲中完成其绘图操作,并将该部分传输到显示屏。 对需要渲染的所有UI区域重复此操作 - 这将更新和传输数据的方式变更为:

更新1、传输1、更新2、传输2、更新3、传输3、……、更新N、传输N

在某些情况下,可以在更新下一个缓冲区的同时进行一个部分帧缓冲区的传输。

显示屏画面撕裂

相比于使用完全帧缓冲区,在使用部分帧缓冲区时,TouchGFX会尽快传输更新过的UI部分。 由于显示屏需要定期刷新,在最多16 ms后(就60 fps显示屏而言),显示屏将在其屏幕上显示接收到的更新。 因此,在所有更新传输完毕之前,对显示屏的最初更新可能会被用户注意到。

如果绘图操作和传输的完整序列需要花费较长时间才能完成(> 16 ms),则用户很可能会看到上一帧与一些新的更新的组合。 这被称为画面撕裂,是不期望发生的。 因此,部分帧缓冲区不适合使用复杂动画、需要长时间渲染的应用。

显示屏更新示例

在讨论如何在应用中配置部分帧缓冲区之前,我们先来看一个具体示例,该示例是一个数字时钟,用移动的圆弧来代表秒数。 绿色圆弧每秒移动6度,一分钟完成一整圈。 用如下图所示的四个控件构建UI:

以下是更新数字时钟和圆弧的代码:

MainView.cpp
void MainView::handleTickEvent()
{
ticks++;
if (ticks == 10)
{
ticks = 0;
secs += 1;
if (secs == 60) //increment minutes
{
secs = 0;
min += 1;
if (min == 60) //increment hours
{
min = 0;
hour += 1;
if (hour == 24)
{
hour = 0;
}
}
//Only update digital clock when minutes or hours change
digitalClock.setTime24Hour(hour, min, secs);
}
//Always update seconds
circleSeconds.updateArc(secs*6 - 20, secs*6);
}
}

下图所示为在圆弧接近顶点和数字时钟更新时前几秒更新的区域(灰色矩形)。 在前两帧中,只有秒数在变化(58和59秒)。 在第三帧中,秒数达到60,小时和分钟文本更新:

上面第三幅图像中更新的矩形为154 x 60像素、 20 x 12像素和33 x 8像素。 在使用标准帧缓冲区时,这些矩形会被存入完整帧缓冲区中(覆盖之前的像素),完整帧缓冲区随后被传输到显示屏。 在使用部分帧缓冲区时,这三个矩形会被存入它们自己的小帧缓冲区中,随后立即被传输到显示屏并显示。

配置部分帧缓冲区

The following steps are required for TouchGFX to use partial frame buffers:

  1. 创建帧缓冲区分配器对象,并分配存储空间
  2. 配置TouchGFX HAL类以使用该分配器
  3. 写入代码以将缓冲区传输至显示屏

步骤1和2由TouchGFX Generator通过STM32CubeMX自动生成,而步骤3是一个专用驱动程序,用于将像素数据传输至显示屏。

Further reading
Read the TouchGFX Generator User Guide for information about how to configure partial frame buffers.

An example configuration with 3 partial framebuffer block of size 1920bytes used to draw the updates areas from the above section is shown below.

首先来看分配用于绘制小圆圈更新的两个帧缓冲区的位置和大小(上面第二幅图)(假设24Bpp):

矩形xy宽度高度像素
矩形1112562214308像素 = 924字节
矩形2153422911319像素 = 957字节

这些矩形都很小,可以放入由帧缓冲区分配器分配的块中。

在上面的第三幅图中,更新了3个矩形:小矩形更新圆圈,较大的矩形覆盖文本:

矩形xy宽度高度像素
矩形1126512012240像素 = 720字节
矩形216542338264像素 = 792字节
矩形3118165154609,240像素 = 27,720字节

同样地,矩形1和2很小,可以放入由帧缓冲区分配器分配的块中,但帧缓冲区3过大。 此矩形过大,将被分成多个可放入帧缓冲区(11,700字节)的矩形。

这里即将更新的第3个矩形太大,无法直接存入最后第3个缓冲区块。 在这种情况下,TouchGFX将等待第一个块传输完毕,然后重复使用第一个块。

将帧缓冲区传输到屏幕

The implementation of the code to transfer partial framebuffer blocks to the display depends on the display interface use. For examples on how this can be done, see the following articles:

结论

在本文中,我们讨论了对于显示屏具有集成帧缓冲存储器的平台而言,部分帧缓冲区策略如何降低其存储空间要求。