Double Buffering

Learn the essentials of double buffering in C++ with practical examples and SDL2 specifics to improve your graphics projects
This lesson is part of the course:

Game Dev with SDL2

Learn C++ and SDL development by creating hands on, practical projects inspired by classic retro games

Free, Unlimited Access
Abstract art representing computer programming
Ryan McCombe
Ryan McCombe
Updated

In the previous lesson, we described a typical game loop:

  1. Get input
  2. Update all our objects
  3. Render the frame

The transition from step 2 to step 3 warrants some further explanation. There is some theory here that is very important to understanding how SDL, and real-time graphics in general, works.

In computer graphics, movement is simulated by quickly showing a sequence of still images, typically called "frames".

A lot of complexity and clever algorithms can go into generating these frames. After all the processing, we are left with a collection of pixel colors that we want to display to the user.

Buffers

A buffer is an area of memory where data is stored for a short period. Frames are buffers - we can think of them as big arrays of pixels. Each pixel represents a color by combining integers representing the red, green, and blue components:

struct Pixel {
  int Red;
  int Green;
  int Blue;
}

std::vector<Pixel> Buffer;

The fundamental goal of computer graphics is to determine which color each pixel needs to be, and to write those values into the buffer.

However, this process takes time. It can take many statements, function calls, and calculations to determine the color of a single pixel, and a typical buffer can have millions of such pixels.

That presents a problem: we don’t want users to witness the frame being constructed. That would break the illusion, so instead, we came up with double buffering.

Double Buffering

With double buffering, as expected, there are two buffers. There’s the buffer the user is seeing, and the one we’re building to show to them next.

The buffer the user is seeing is called the front buffer, and the one we’re building is the back buffer. When we’ve finished building the back buffer, we swap them around.

The back buffer becomes the front buffer and is shown to our users. The front buffer becomes the back buffer, and we start creating our next frame in it.

This is typically managed on the GPU but, were we to create the effect in C++, we could imagine it being something like this:

using Buffer = std::vector<Pixel>;
Buffer A;
Buffer B;

Buffer* Front { &A };
Buffer* Back { &B };

while(true) {
  DrawEnvironment(Back);
  DrawCharacters(Back);
  DrawUI(Back);
  Swap(Front, Back);
}

The using statement in the first line of this code is an example of a type alias. It allows us to create a friendlier, alternative name for a more complex type. We cover type aliases in more detail in the next module.

Double Buffering in SDL2

In SDL2, the double buffering is mostly handled for us. When we use commands like SDL_FillRect(), SDL ensures this is being done on the back buffer.

However, we do need to tell SDL when it needs to swap buffers. When we’re working with window surfaces, that command is SDL_UpdateWindowSurface(), which we’ve been using in our Window class in previous lessons.

If we’re making changes in our application, and those changes do not seem to be appearing, the first thing we should do is make sure we’re calling SDL_UpdateWindowSurface() at the appropriate times.

In our example application loop from the previous lesson, we were calling this on every iteration of our loop.

Screen Tearing and VSync

These two topics are generally beyond the scope of this course, but they closely relate to the concept of graphics buffers, and may be of interest to those who want some more context on what they are.

Once our software implements double buffering, there’s an additional layer of complexity introduced by hardware constraints. Computer monitors also don’t update their display all at once.

If we trigger our buffer swap at the same time the monitor is updating, content from two or more frames can be shown on the screen at once. This is commonly called "screen tearing".

Screenshot showing screen tearing

There are various options for dealing with this, a popular one being synchronizing the application’s refresh rate with the monitor’s refresh rate. Variations of this idea are commonly called vertical sync (VSync).

Triple Buffering

When our refresh rate is synchronized with the hardware’s refresh rate, this presents yet another problem for us. When we complete the next frame, it needs to wait on the back buffer until the monitor is ready for it.

So, we have completed frames in both our buffers. We’re ready to start working on a new frame, but we have nowhere to create it. Predictably, this is the use case for adding a third buffer to the process. With triple buffering, we have:

  1. The front buffer, which the user is currently seeing
  2. The middle buffer, which has a completed frame ready to be used the next time the monitor updates
  3. The back buffer, where we’re creating the frame to come after that

This is the reason VSync-based implementations can sometimes feel less responsive. They can have increased input latency - the time between the user performing an input, and that input being reflected on the screen.

We react to user inputs on the back buffer. With double buffering, that means those inputs are reflected on the next frame. With triple buffering, there’s potentially an additional frame (the middle buffer) between what is currently on screen, and the frame that implements the user’s input.

Summary

This lesson provides an overview of double buffering - the key mechanism that allows for real-time graphics. Double buffering involves two images, or buffers: a front buffer for display and a back buffer for rendering the next frame. Here are the key points covered:

  • Double Buffering Basics: Double buffering involves using a visible front buffer and a hidden back buffer to prepare the next frame.
  • Implementation in C++: We explore a simulated C++ environment using type aliases for buffer management and illustrate how to swap buffers.
  • SDL2 Application: Double buffering in SDL2 is automated, but requires explicit calls to SDL_UpdateWindowSurface() to manage buffer swaps.
  • Screen Tearing and VSync: Screen tearing occurs when buffer swaps align poorly with monitor refreshes, which can be mitigated using vertical sync (VSync).
  • Triple Buffering: Triple buffering adds a third buffer to reduce input latency and manage timing issues between user inputs and screen updates.

Was this lesson useful?

Next Lesson

Keyboard and Mouse Input in SDL2

Learn how SDL2 handles user input, and how we can react to it to update our application in real time.
aSDL8.jpg
Ryan McCombe
Ryan McCombe
Updated
Lesson Contents

Double Buffering

Learn the essentials of double buffering in C++ with practical examples and SDL2 specifics to improve your graphics projects

sdl2-promo.jpg
This lesson is part of the course:

Game Dev with SDL2

Learn C++ and SDL development by creating hands on, practical projects inspired by classic retro games

Free, Unlimited Access
sdl2-promo.jpg
This lesson is part of the course:

Game Dev with SDL2

Learn C++ and SDL development by creating hands on, practical projects inspired by classic retro games

Free, unlimited access

This course includes:

  • 27 Lessons
  • 100+ Code Samples
  • 91% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Next Lesson

Keyboard and Mouse Input in SDL2

Learn how SDL2 handles user input, and how we can react to it to update our application in real time.
aSDL8.jpg
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved