Multiple Buffering
Multiple buffering is a method of using more than one frame buffer. Basically it works as follows: With multiple buffers enabled there is a front buffer which is used by the display controller to generate the picture on the screen and one or more back buffers which are used for the drawing operations. After completing the drawing operations the back buffer becomes the visible front buffer.
With two buffers, one front and one back buffer, it is normally called ’double buffering’, with two back buffers and one front buffer it is called ’triple buffering’.
In general it is a method which is able to avoid several unwanted effects:
- The visible process of drawing a screen item by item.
- Flickering effects caused by overlapping drawing operations.
- Tearing effects caused by writing operations outside the vertical blanking period.
The following section explains in detail how it works, the requirements to be able to use this feature, how to configure emWin and the advantage of ’triple buffering’ against ’double buffering’. Further it explains how to configure the optional Window Manager for automatic use of ’multiple buffering’.
How it works
Multiple buffering is the use of more than one frame buffer, so that the display ever shows a screen which is already completely rendered, even if a drawing operation is in process. When starting the process of drawing the current content of the front buffer is copied into a back buffer. After that all drawing operations take effect only on this back buffer. After the drawing operation has been completed the back buffer becomes the front buffer. Making the back buffer the visible front buffer normally only requires the modification of the frame buffer start address register of the display controller.
Now it should be considered that a display is beeing refreshed continuously by the display controller app. 60 times per second. After each period there is a vertical syncronisation signal, normally known as VSYNC signal. The best moment to make the back buffer the new front buffer is this signal. If not considering the VSYNC signal tearing effects can occur.
Tearing effect:

Double buffering
With double buffering only 2 buffers are available: One front and one back buffer. When starting the drawing operation the current content of the front buffer is copied into the back buffer. After completing the operation the back buffer should become the visible front buffer.
As explained above the best moment for doing this is reacting on the VSYNC signal of the display controller. Here the disadvantage of double buffering against tripple buff- ering is revealed: Either the frame buffer start address is changed immediately at the end of the drawing operation or after waiting until the next VSYNC signal. This means that either tearing effects could occur or the performance slows down because of waiting for the next VSYNC signal.
Triple buffering
As the name implies there are 3 buffers available: One front and 2 back buffers. When starting the drawing operation the current content of the front buffer is copied into the first back buffer. After completing the operation the back buffer should become the visible front buffer. Contrary to the double buffer solution it is not required to switch to the buffer immediately. Switching to the new front buffer could be done on the next VSYNC signal of the display contoller which can be achieved by an interrupt service routine (ISR). Most of the display controllers which are able to deal with more than one frame buffer provide the VSYNC signal as interrupt source. Within the ISR the pending front buffer should become visible. Until the pending front buffer becomes visible it is not used for further drawing operations. If a further draw- ing operation is initiated before the pending front buffer has become visible the sec-ond back buffer is used for the drawing operation. If a new buffer is ready until waiting for the VSYNC signal it becomes the new pending front buffer and so on. This always protects the front buffer against writing operations.
It should be mentioned that changing the display buffer start address on some dis- play controllers takes only effect when drawing the next frame. In this case the solu- tion without ISR works as well as without ISR. Only if changing the address takes effekt directly an ISR is required to avoid tearing effects.
Requirements
The following list shows the requirements for using multiple buffers:
- The display controller should support multiple frame buffers.
- Enough video RAM for multiple frame buffers should be available.
- If tearing effects should be avoided it should be possible to react on the VSYNC signal of the display controller and tripple buffering is recommended to achieve the best performance.
Limitations
Unfortunately it is not possible to use virtual screens together with multiple buffer support.
Configuration
In general there are 2 routines in the configuration file LCDConf.c which need to be modified, the display configuration routine LCD_X_Config() and the driver callback function LCD_X_DisplayDriver().
LCD_X_Config()
Basically one thing needs to be done here: Enabling the use of multiple buffers.
Basic configuration
The first thing which has to be done before creating the display driver device is con- figuring the multiple buffer interface. This is normally done in LCD_X_Config(). It is strictly required to enable multiple buffering before creating the display driver device as shown in the following code snippet:
void LCD_X_Config(void) {
//
// Initialize multibuffering
//
GUI_MULTIBUF_Config(NUM_BUFFERS);
//
// Set display driver and color conversion
//
GUI_DEVICE_CreateAndLink(DISPLAY_DRIVER, COLOR_CONVERSION, 0, 0);
...
}
Custom callback routine for copying the buffers
Further a callback routine for copying the buffers can be set. As explained above at the beginning of the drawing operation it is required to copy the content of the cur- rent front buffer to the back buffer. Normally a simple memcpy operation is used to do this. But if the used display controller for example consists of a BitBLT-engine which is able to do the copy operation it could be desired to use it for the copy oper- ation. Or a DMA based routine should be used to do the copy operation. In these cases a custom defined callback function can be used for this operation. It can be installed after creating the display driver device as shown in the following code snippet:
static void _CopyBuffer(int LayerIndex, int IndexSrc, int IndexDst) {
unsigned long BufferSize, AddrSrc, AddrDst;
//
// Calculate the size of one frame buffer
//
BufferSize = (XSIZE * YSIZE * BITSPERPIXEL) / 8;
//
// Calculate source- and destination address
//
AddrSrc = _VRamBaseAddr + BufferSize * IndexSrc;
AddrDst = _VRamBaseAddr + BufferSize * IndexDst;
memcpy((void *)AddrDst, (void *)AddrSrc, BufferSize);
}
void LCD_X_Config(void) {
//
// Initialize multibuffering
//
GUI_MULTIBUF_Config(NUM_BUFFERS);
//
// Set display driver and color conversion
//
GUI_DEVICE_CreateAndLink(DISPLAY_DRIVER, COLOR_CONVERSION, 0, 0);
//
// Set custom callback function for copy operation
//
LCD_SetDevFunc(0, LCD_DEVFUNC_COPYBUFFER, (void (*)())_CopyBuffer);
} Please note that the above sample implementation normally makes no sense, because a simple memcpy() operation is the default behavior of the driver. It makes only sense to use a custom callback function if there is any accelleration option which should be used.
LCD_X_DisplayDriver()
After the drawing process has been completed the back buffer should become visible. The display driver sends a LCD_X_SHOWBUFFER command to the display driver callback function. The callback function then has to react on the command and should make sure that the buffer becomes visible. This can be done either by an ISR or by directly writing the right address into the frame buffer start address of the display controller.
With ISR
The following code snippet shows a sample implementation:
static void _ISR_EndOfFrame(void) {
unsigned long Addr, BufferSize;
if (_PendingBuffer >= 0) {
//
// Calculate address of the given buffer
//
BufferSize = (XSIZE * YSIZE * BITSPERPIXEL) / 8;
Addr = _VRamBaseAddr + BufferSize * pData->Index;
//
// Make the given buffer visible
//
AT91C_LCDC_BA1 = Addr;
//
// Send a confirmation that the buffer is visible now
//
GUI_MULTIBUF_Confirm(_PendingBuffer);
_PendingBuffer = -1;
}
}
int LCD_X_DisplayDriver(unsigned LayerIndex, unsigned Cmd, void * p) {
switch (Cmd) {
case LCD_X_SHOWBUFFER:
LCD_X_SHOWBUFFER_INFO * pData;
pData = (LCD_X_SHOWBUFFER_INFO *)p;
//
// Remember buffer index to be used by ISR
//
_PendingBuffer = pData->Index;
break;
...
}
} The above implementation assumes the existence of an ISR which is executed at the next VSYNC signal.
Without ISR
If there is no ISR available alternatively the address can be set directly with the dis- advantage that tearing effects could occur. The following code snippet shows a sample implementation:
int LCD_X_DisplayDriver(unsigned LayerIndex, unsigned Cmd, void * p) {
unsigned long Addr, BufferSize;
switch (Cmd) {
...
case LCD_X_SHOWBUFFER:
LCD_X_SHOWBUFFER_INFO * pData;
pData = (LCD_X_SHOWBUFFER_INFO *)p;
//
// Calculate address of the given buffer
//
BufferSize = (XSIZE * YSIZE * BITSPERPIXEL) / 8;
Addr = _VRamBaseAddr + BufferSize * pData->Index;
//
// Make the given buffer visible
//
AT91C_LCDC_BA1 = Addr;
//
// Send a confirmation that the buffer is visible now
// GUI_MULTIBUF_Confirm(pData->Index);
break;
...
}
} Automatic use of multiple buffers with the WM
The optional window manager (WM) is able to use the multiple buffer feature automatically. The function WM_MULTIBUF_Enable() can be used to enable this function. If enabled the WM first switches to the back buffer before redrawing the invalid win- dows. After drawing all invalid windows the new screen becomes visible. This hides the process of drawing a screen window by window.
Multiple buffer API
The following table lists the available routines of the multiple buffer support.
| Routine | Description |
|---|---|
| Basic functions | |
| GUI_MULTIBUF_Begin() | Needs be called before starting the drawing operation. |
| GUI_MULTIBUF_BeginEx() | Same as above except the parameter LayerIndex. |
| GUI_MULTIBUF_Config() | Needs to be called to configure the use of multiple buffers. |
| GUI_MULTIBUF_ConfigEx() | Same as above except the parameter LayerIndex. |
| GUI_MULTIBUF_Confirm() | Should be called immediately after the pending front buffer has become visible. |
| GUI_MULTIBUF_ConfirmEx() | Same as above except the parameter LayerIndex. |
| GUI_MULTIBUF_End() | Needs be called after completing the drawing operation. |
| GUI_MULTIBUF_EndEx() | Same as above except the parameter LayerIndex. |
| GUI_MULTIBUF_GetNumBuffers() | Returns the number of used buffers. |
| GUI_MULTIBUF_GetNumBuffersEx() | Same as above except the parameter LayerIndex. |
| Optional window manager functions | |
| WM_MULTIBUF_Enable() | Enables or disables the automatic use of multiple buffers by the optional WM. |
Multiple Buffering
