Double-bufffering and frame-switching¶
In this chapter, we see that directly modyfing the frame that is displayed on the screen leads to tearing/flickering and that double-buffering should be used instead.
Starting from the code of the previous chapter, suppose that we modify the contents of the frame continuously as we do here:
let render frame col = do
-- fill a frame with a color
forEachGenericFramePixel frame 0 \_x _y ptr -> do
poke ptr (col :: Word32)
renderLoop col = do
render frame col
renderLoop (col + 10) -- change the color for each frame
sysFork "Render loop" (renderLoop 0)
If we try to execute this example, we see some flickering: sometimes the displayed frame is not fully repaint and it has two different colors, that’s why we see some vertical line demarcating both colors.
This is a common issue that can be solved either by:
only doing the rendering during the vblank period
using two different frames and switching them during the vblank period
The first solution only requires a single buffer but your application has to render each frame very fast during the vblank period and before the end of the refresh cycle.
Using double-buffering is easier. Source code of the modified code example using double-buffering. The change consists in allocating two frames, rendering in one when the other is displayed and switching the frames when the rendering is over:
let switchFrame frame = assertLogShowErrorE "Switch frame" <| do
configureGraphics card Commit EnableVSync DisableFullModeset do
setPlaneSource plane frame
frame1 <- createGenericFullScreenFrame card mode pixelFormat 0
frame2 <- createGenericFullScreenFrame card mode pixelFormat 0
let renderLoop b col = do
let frame = if b then frame1 else frame2
render frame col
switchFrame frame
renderLoop (not b) (col + 10)
sysFork "Render loop" (renderLoop False 0)
When we execute this second example, the displayed frame is always fully rendered and we don’t get the flickering effect.