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 ⅛th 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 depth | Framebuffering strategy | Memory usage |
---|---|---|---|
320x240 | 16 bpp | Double buffering | 300 KB |
320x240 | 16 bpp | Single buffering | 150 KB |
320x48 (320x240/5) 1 | 16 bpp | Partial buffering | 30 KB |
320x24 (320x240/10) 1 | 16 bpp | Partial buffering with two partial buffers | 30 KB |
320x1 (320x240/240) 1 | 16 bpp | Partial buffering | 640 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
Qt Quick Ultralite core calls beginFrame and endFrame once for each area that needs a visual update. Updated area is given with rect
in PlatformContext::beginFrame.
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:
- First update
- 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:
- Partial framebuffer content during second update, containing the next part of the dirty area.
- 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:
- Partial framebuffer content for third and last part of the dirty area.
Implementing partial framebuffering support
Adding a partial framebuffer
In this example ⅛th of the display size is used as the partial framebuffer size. dirtyRect
is added for storing current dirty rectangle for display flushing.
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 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 of these ports has to be rebuilt with the CMake option QUL_PLATFORM_PARTIAL_FRAMEBUFFER 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.
Available under certain Qt licenses.
Find out more.