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