C

部分帧缓冲区

概述

通常,帧缓冲区包含显示上的所有像素。支持层级的平台上,应用程序可能包含多个小于显示大小的层。

提供独立显示内存的平台上,可以使用部分帧缓冲区来存储需要重新绘制的像素(称为脏矩形)。

部分帧缓冲区策略为每个需要重新绘制区域提供一个脏矩形,最大限度地减少需要传输到显示器的数据量。

可以使用单个或多个部分缓冲区。多个部分缓冲区允许在后部分帧缓冲区上进行渲染,同时前部分帧缓冲区正在刷新。

或者,在没有独立显示内存的单全帧缓冲区平台上使用部分缓冲区可以减少渲染伪影。首先在部分缓冲区中渲染,然后将完全渲染的内容复制到全显示帧缓冲区。然而,在这种情况下使用部分缓冲区可能会为不完全适应部分帧缓冲区的移动元素引入撕裂效果。

内存使用

与单缓冲区和双缓冲区图形相比,部分缓冲区可以显著减少内存使用量。

部分帧缓冲区的最小像素计数为层的宽度。虽然使用较高的像素计数可以减少渲染开销,但它会增加内存需求。

部分帧缓冲区的最佳大小取决于平台。您可以从全分辨率大小的 1/8 开始,然后根据需求逐步调整大小。

部分帧缓冲区的内存需求可以表示如下

Memory usage in bytes = width x height x bytes per pixel x number of buffers

以下是一些示例分辨率和部分帧缓冲区策略组合的内存使用需求

层分辨率(宽度 x 高度)颜色深度帧缓冲区策略内存使用
320x24016 bpp双缓冲区300 KB
320x24016 bpp单缓冲区150 KB
320x48 (320x240/5) 116 bpp部分缓冲区30 KB
320x24 (320x240/10) 116 bpp带有两个部分缓冲区的部分缓冲区30 KB
320x1 (320x240/240) 116 bpp部分缓冲区640 B

注意:1 部分组帧缓存的分辨率不固定,它表示单个部分帧缓存的像素数的最大值。像素数

使用部分帧缓存进行渲染

单/双缓冲图形渲染

在帧更新期间,Qt Quick Ultralite Core 对每个 调用 beginFrameendFrame。将这些脏区域渲染到帧缓冲区是在这些函数调用之间完成的。

beginFrame 中的 rect 参数指定将要更新的脏区域。 rect 表示给定层中所有脏区域合并后的边界矩形。

对于每个层最后调用的是 presentFrame 方法。通常,此函数交换前后帧缓冲区,以便在显示屏上显示新内容。在单缓冲图形中,可能会观察到部分渲染的UI元素,特别是移动的UI元素,这可能导致闪烁。

以下伪代码说明了Qt Quick Ultralite Core如何调用平台自适应函数。在这里,render 代表一种渲染操作,即通过贴图或混合新内容到帧缓冲区。

beginFrame(layer X, boundingRect(dirtyRects A|B|... ))
render(dirtyRect A)
...
render(dirtyRect B)
...
endFrame(layer X)

beginFrame(layer Y, ...)
...
endFrame(layer Y)

...

presentFrame(boundingRect(dirtyRects A|B|...)) // swap buffers if double buffered

部分缓冲图形的渲染

在使用部分帧缓冲区策略时,Qt Quick Ultralite Core 对给定 中的每个脏区域调用 beginFrameendFrame

根据部分帧缓冲区像素大小,Qt Quick Ultralite Core 通过调用 beginFrame 中的输入参数 rect 将脏区域的更新拆分为矩形,然后在它们中绘制。

以下伪代码说明了此概念。如上所述,render 代表一种渲染操作,即通过贴图或混合新内容到部分帧缓冲区。

beginFrame(layer X, dirtyRect A)
render(dirtyRect A)
...
endFrame(layer X) // flush partial framebuffer to display

beginFrame(layer X, dirtyRect B)
render(dirtyRect B)
...
endFrame(layer X) // flush partial framebuffer to display

...

beginFrame(layer Y, ...)
...
endFrame(layer Y) // flush partial framebuffer to display

...

presentFrame(boundingRect(dirtyRects A|B|...))

示例

在以下示例中,我们展示了当用户通过在Qt Quick Ultralite Thermostat Demo的一个Room视图中按下按钮与应用程序进行交互时,显示内存和部分帧缓存内容。

以下图像显示了在按下按钮之前,应用程序空闲时显示内存内容

平台自适应使用部分帧缓冲区策略,部分帧缓冲区大小为 16320 像素。Qt Quick Ultralite Core 通过调用 partialBufferPixelCount 函数计算出对脏区域所需的 最大区域。

当按下底部箭头以设置较低温度时,将触发一个重新绘制事件以更新较底部的按钮区域。用于照亮的按钮的脏区域总面积为 285x132=37620 像素,必须将其分成3个脏矩形,以便每个都能适应部分帧缓冲区。

以下是对用户交互后的更新阶段进行描述,展示了显示内存内容和部分帧缓存内容

  1. 首次更新
    • 当按下底部箭头后调用 endFrame 时的部分帧缓存内容

      x: 258, y: 291, width: 285 and height: 57 (total 16245 pixels).

    • 在部分帧缓存内容刷新到显示后的显示内存内容endFrame

  2. 第二次更新
    • 第二次更新时的部分帧缓冲区内容,包含脏区域的下一段。

      x: 258, y: 291, width: 285 and height: 57 (total 16245 pixels).

    • 刷新后的显示内存内容。

  3. 第三次更新。
    • 脏区域的第三次(也是最后一次)更新的部分帧缓冲区内容。

      x: 258, y: 405, 宽度: 285 和高度: 18(总共 5130 个像素).

    • 将最后一段刷新到显示器的显示内存内容。

实现部分帧缓冲区支持

添加部分帧缓冲区

在此示例中,1/8 的显示尺寸用作部分帧缓冲区大小。 dirtyRect 添加用于存储用于显示刷新的当前脏矩形。

namespace Qul {
namespace Platform {
namespace Private {
static constexpr uint16_t displayWidth = 320;
static constexpr uint16_t displayHeight = 240;
static constexpr size_t bufferPixelCount = displayWidth * displayHeight / 8;
static constexpr uint8_t bytesPerPixel = 2;
static uchar framebuffer[bufferPixelCount * bytesPerPixel];
PlatformInterface::Rect dirtyRect;
} // namespace Private

设置帧缓冲区类型

为了告知 Qt Quick Ultralite Core 所使用的帧缓冲区策略,平台适配应返回 PartialBuffering 作为 帧缓冲区类型

FrameBufferingType frameBufferingType(const PlatformInterface::LayerEngine::ItemLayer *) const override
{
    return PartialBuffering;
}

部分缓冲区大小

对于 partialBufferPixelCount 的平台适配代码应返回以像素为单位的部分帧缓冲区大小,以计算脏矩形的大小。

size_t partialBufferPixelCount(const PlatformInterface::LayerEngine::ItemLayer *) const override
{
    return Private::bufferPixelCount;
}

为 DrawingDevice 设置大小和每行字节

beginFrame 中的 rect 指定了将要绘制到其上的脏区域。基于 rect 的宽度和高度设置 Qul::PlatformInterface::DrawingDevicesize每行字节

平台适配应在 endFrame 中稍后使用存储此矩形。

PlatformInterface::DrawingDevice *beginFrame(const PlatformInterface::LayerEngine::ItemLayer *,
                                             const PlatformInterface::Rect &rect,
                                             int refreshInterval) override
{
    static PlatformInterface::DrawingEngine drawingEngine;

    static PlatformInterface::DrawingDevice buffer = {Qul::PixelFormat_RGB16,
                                                      PlatformInterface::Size(Private::displayWidth,
                                                                              Private::displayHeight),
                                                      Private::framebuffer,
                                                      Private::displayWidth * Private::bytesPerPixel,
                                                      &drawingEngine};

    Private::dirtyRect = rect;

    buffer.setSize(PlatformInterface::Size(rect.width(), rect.height()));
    buffer.setBytesPerLine(rect.width() * Private::bytesPerPixel);
    buffer.setBits(Private::framebuffer);

    return &buffer;
}

将帧缓冲区内容刷新到显示器

传递给 beginFrame并由绘制引擎绘制到其上的脏矩形稍后用于 endFrame 以刷新更新的部分帧缓冲区内容到显示器。

void endFrame(const PlatformInterface::LayerEngine::ItemLayer *) override
{
    // Lcd_Flush(Private::dirtyRect.x(),
    //    Private::dirtyRect.y(),
    //    Private::dirtyRect.x() + Private::dirtyRect.width(),
    //    Private::dirtyRect.y() + Private::dirtyRect.height(),
    //    Private::framebuffer);
}

调整脏矩形以满足平台要求

使用 adjustedPartialUpdateRect ,平台适配可以调整稍后传递给 beginFrame 的脏矩形。它使平台适配可以根据特定的平台需求调整矩形,例如,数据对齐、刷新区域的最低大小等。

注意:此方法的实现是可选的。

PlatformInterface::Rect adjustedPartialUpdateRect(const PlatformInterface::LayerEngine::ItemLayer *layer,
                                                  const PlatformInterface::Rect &rect) const override
{
    auto r = PlatformInterface::Rect(rect);

    // r.setX(...);
    // r.setWidth(...);

    return r;
}

支持的平台

有关支持部分帧缓冲区的参考端口的列表,请参阅 支持的功能

注意:平台库必须重新编译才能启用此功能。有关更多信息,请参阅 针对评估包构建 Qt Quick Ultralite 平台库

另请参阅 将图形显示在屏幕上QUL_PLATFORM_PARTIAL_FRAMEBUFFER

在特定 Qt 许可证下提供。
了解更多信息。