C

Partial framebuffer

Overview

Typically, a framebuffer holds all the pixels that are shown on the display. On platforms that support layers, an application may contain multiple layers that are smaller than the size of the display.

On platforms that offer a separate memory for the display, a partial framebuffer can be used to store just the pixels that need to be repainted (referred to as dirty rectangle).

The partial framebuffer strategy provides a dirty rectangle for each repainted area, minimizing the amount of data that needs to be transferred to the display.

Single or multiple partial buffers can be used. Multiple partial buffers allow rendering on the back partial framebuffer, while the front partial framebuffer is being flushed.

Alternatively, partial buffering can be used to reduce rendering artifacts on platforms that do not have a separate display memory and use a single full framebuffer. Partially rendered content can be reduced by rendering first in the partial buffer and then by copying the fully rendered content to the full display framebuffer. However, using partial buffering in this case might introduce tearing effect for moving elements that do not fully fit into the partial framebuffer.

Memory usage

Partial buffering can significantly reduce memory usage compared to single and double buffered graphics.

The minimum pixel count for the partial framebuffer is the width of the layer. Although using a higher pixel count reduces rendering overhead, it increases memory requirements.

The optimal size for the partial framebuffer is platform dependent. You can start with with 1/8th of the full resolution size, and gradually adapt the size to your needs.

The memory requirements for the partial framebuffer can be expressed as follows:

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

The following are the memory usage requirements for a few example combinations of resolution and partial framebuffering strategy:

Layer resolution (width x height)Color depthFramebuffering strategyMemory usage
320x24016 bppDouble buffering300 KB
320x24016 bppSingle buffering150 KB
320x48 (320x240/5) 116 bppPartial buffering30 KB
320x24 (320x240/10) 116 bppPartial buffering with two partial buffers30 KB
320x1 (320x240/240) 116 bppPartial buffering640 B

Note: 1 Resolution of a partial framebuffer is not fixed and it indicates the maximum pixel count of a single partial framebuffer.

Rendering with Partial framebuffer

Rendering of single/double buffered graphics

During a frame update the Qt Quick Ultralite Core calls beginFrame and endFrame for each layer. Rendering dirty areas to the framebuffer is done between these function calls.

The rect argument in beginFrame specifies the dirty area that will be updated. rect represents a bounding rectangle of all dirty areas combined together for a given layer.

The presentFrame method is called last for each layer. Typically, this function swaps front and back framebuffers to show the new content on the display. On single buffered graphics, partially rendered UI elements might be observed, especially for moving UI elements which can cause flickering.

The following pseudo code illustrates how platform adaptation functions are called by Qt Quick Ultralite Core. Here, render represents a rendering action, either by blitting or blending new content to the framebuffer.

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

Rendering of partial buffered graphics

When using a partial framebuffer strategy, the Qt Quick Ultralite Core calls beginFrame and endFrame for every dirty area in a given layer.

Depending on the partial framebuffer size in pixels, Qt Quick Ultralite Core splits the updates of a dirty area in rectangles (rect input argument in beginFrame) that fit into the partial framebuffer, and then draws into them.

The following pseudo code illustrates this concept. As above, render represents a rendering action, either by blitting or blending new content to the partial framebuffer.

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|...))

Example

In the following example, we show display memory and partial framebuffer content when a user interacts with the application by pressing a button in one of the Room views of the Qt Quick Ultralite Thermostat Demo.

The following image shows the display memory content when the application is idle, before the button is pressed:

The platform adaptation is using the partial framebuffer strategy, with a partial framebuffer size of 16320 pixels. Qt Quick Ultralite Core calculates the maximum area required for the dirty regions by calling the partialBufferPixelCount function.

When the bottom arrow is pressed to set a lower temperature, a repaint event is triggered to update the lower button area. The total dirty area for the illuminated button is a rectangle of size 285x132=37620 pixels, which must be split into 3 dirty rectangles to fit each into the partial framebuffer.

The following is a description of the update stages following user interaction, showing both display memory content and partial framebuffer content:

  1. First update
    • Partial framebuffer content when endFrame is called after bottom arrow has been pressed:

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

    • Display memory content after partial framebuffer content is flushed to the display in endFrame:

  2. Second update
    • Partial framebuffer content during second update, containing the next part of the dirty area.

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

    • Display memory content after flushing:

  3. Third update
    • Partial framebuffer content for third and last part of the dirty area.

      x: 258, y: 405, width: 285 and height: 18 (total 5130 pixels).

    • Display memory content after last part is flushed to the display:

Implementing partial framebuffering support

Adding a partial framebuffer

In this example 1/8th of the display size is used as the partial framebuffer size. dirtyRect is added for storing current dirty rectangle for display flushing.

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

Setting framebuffering type

To inform Qt Quick Ultralite Core about the framebuffering strategy in use, the platform adaptation should return PartialBuffering as the framebuffering type.

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

Partial buffer size

The platform adaptation code for partialBufferPixelCount should return the partial framebuffer size in pixels, to compute the dirty rectangle size.

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

Setting size and bytes per line for DrawingDevice

The rect in beginFrame specifies the dirty area that will be drawn to. The Qul::PlatformInterface::DrawingDevice size and bytes per line are set based on width and height of rect.

The platform adaptation should store this rectangle for later use in 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;
}

Flushing framebuffer content to the display

The dirty rectangle passed to beginFrame then drawn into by the DrawingEngine, is later used in endFrame to flush the updated partial framebuffer content to the display.

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);
}

Adjusting dirty rectangle to platform requirements

Using adjustedPartialUpdateRect the platform adaptation can adjust the dirty rectangle that will be later passed to beginFrame. It enables the platform adaptation to adjust the rectangle based on the platform-specific needs, such as, data alignment, minimum size of the flushed area, and so on.

Note: The implementation of this method is optional.

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;
}

Supported platforms

Refer to Supported Features for a list of reference ports that support the partial framebuffer.

Note: Platform library has to be rebuilt to enable this feature. See Building Qt Quick Ultralite platform library against the evaluation packages for more information.

See also Getting graphics on the screen and QUL_PLATFORM_ENABLE_PARTIAL_FRAMEBUFFER.

Available under certain Qt licenses.
Find out more.