The double buffer behavioral pattern allows multiple changes to all appear to occur at the same time.
This is a simple pattern that’s used anytime you need not just synchronization between readers of information and writers of that same information but when you need the readers to have access to the current information while the writers are updating a new version. Once everything is ready, then the next version becomes the current and the old current version is recycled and used for the new next version.
This design pattern also starts a new series of design patterns focused more on game development. This pattern can be found in the book “Game Programming Patterns” by Robert Nystrom. I recommend this book in addition to listening to my audio explanation because the author has an explanation that’s designed for the written page.
The most common application of this pattern in game programming is probably the screen updates. It takes a while to draw each frame as it needs to appear. And this process usually starts with a blank canvas and gets built up bit by bit. Some parts can even get updated multiple times as some image is drawn and then overwritten by some other image. Only once the frame is completely drawn should it be sent to the screen. During the time the new frame is being drawn, it’s possible that the video card will need to be refreshing the screen on its own schedule. By updating a separate buffer, your application can let the video card use the current frame until the new one is ready.
This pattern can help not just with video refreshes. You can also manage class property values in the same way. If you have an object with lots of properties and you want to update some of them but the update really needs to be done all at once in order to make any sense, then this pattern can help. You’ll need to keep two copies of the data in memory with some way to switch between them. The easiest way to switch is to maintain two pointers, a current pointer and a next pointer.
I didn’t mention this in the audio but you can also use this pattern for cancelling updates. Let’s say you have a dialog box where the user can enter different values in data fields. The dialog box will have an Ok and a Cancel button. While changes are being made, put the new values in the next buffer. If the user commits the changes, then swap the buffers. And if the user cancels, then you don’t need to worry about restoring anything because you left all the original data unchanged. The Cancel button just causes the second buffer to be discarded. Or you can just clear it and be ready for another possible update.
Listen to the full episode about this design pattern or you can also read the full transcript below.
The basic description says that this pattern causes multiple operations done sequentially to appear to be done at the same time.
We’ve actually finished going through all the patterns in the Gang of Four book and I’m now explaining various patterns from other books. There’s a really good book called Game Programming Patterns by Robert Nystrom that I recommend that documents several more patterns applicable to game development.
All of these patterns from the Gang of Four to any more recent book about design patterns describe well known and common practices. There’s nothing new or unique about a pattern because if so, then it wouldn’t be a pattern. Even though I’m explaining the patterns, I recommend that you buy a copy of the books I mention because the authors explain the patterns in a way that best fits a written page. The authors can also use diagrams. Anytime you can learn through multiple techniques, you’ll learn better and faster. So make sure to get the books in addition to listening to this podcast.
In the book Game Programming Patterns, this pattern is listed under a group called sequencing patterns. I’ll explain the other patterns in this group later. This is something I don’t fully agree with. I definitely see the reason for creating a new category of patterns but just because you can do something doesn’t mean you should. I plan to discuss this more in tomorrow’s QA Friday. For now, know that I put the double buffer pattern in the behavioral group as described by the Gang of Four. Sure, this pattern relates to time. But it also explains how objects behave.
At the most basic level, what this pattern describes is how to deal with code that needs to read information while at the same time other code needs to change that information.
How is it possible for a computer to do multiple things at the same time? Processors are fast but don’t they still do things one instruction at a time? Well, modern computers can have multiple processors and each processor can have multiple cores. And who knows? Maybe in the future there’ll be a new computer with even more advanced features. We already have hyper threading. I’m sure they’ll be other ways for computers to run code in parallel. All this means that a computer really can be doing multiple things all at the same time. Oh, and there’s also the graphics cards on modern computers to consider. You can think of these as almost separate computers on their own.
There are ways to make sure all these simultaneous activities are coordinated and I’ll explain those in future episodes about multi-threading.
This pattern isn’t so much about coordinating access. It’s more concerned with making multiple changes all appear to happen at the same time. If all we wanted to do was coordinate access, then anytime the code needed to change multiple pieces of information, it could first signal all the other code that might want to access the same data to hold off for a while. If that while turns out to take too much time, then the delay could cause problems. We need a way for the other code to continue using what it had right up to the moment the changes are ready. That’s what this pattern is about.
I’ll explain more right after this message from our sponsor.
( Message from Sponsor )
I’ve never worked for a large auto company making cars and trucks but a little common sense says that a new car design doesn’t just happen overnight. Imagine for a moment the confusion that would happen if a car company had just a single main set of plans for building a car. The factory would start building a model 2016 car which might take several days or even weeks to complete. And at the same time, an engineer would be modifying the plans for the new 2017 year model. I don’t think any of the cars would turn out right. They’ll all be some different combination.
Maybe a small custom shop that only builds 10 cars a year could work like this. It would have to stop production for a while to give the engineers a chance to update the plans. And then the engineers would have to stop designing for a while to let the new cars be built. This is the coordination that thread synchronization provides. The cars are all properly built with no mixups. And this is also why we need a better solution. Not for the mixups. We never want mixups. But to avoid the delays.
Okay, how does this pattern apply to programming? And especially to game development?
A computer has some memory that controls what should be displayed on the screen. A video card is constantly reading this memory to update the screen. What appears to be a fixed image or a movie is made up of a series of screen draws or normally called a series of frames. Things appear to move when they get drawn in a slightly different location from one frame to the next. A game that looks smooth is usually drawing 60 frames per second.
The video card is just going through this memory and sending it to the screen. Think of this like the car factory turning out one car after another. It’s important to make sure that when you want to change what appears on the screen that you change everything at once. This is what happens when a car factory switches to begin building a new model year version.
Most games, especially immersive 3D games, update themselves by starting out fresh each time. The game usually starts with an empty screen each frame and draws one part at a time. Sometimes something that was drawn will need to be drawn over with something else. All this drawing takes time. The last thing you want is for the video card to show this incremental progress to the user on the main screen. If this was the car engineer, this would be like the engineer needing to start each year’s design back in the horseless carriage days. So how do you solve this?
The answer is simple, don’t work with the same data. Instead of updating the main screen image directly with the next frame, do the drawing off screen in another part of memory. This memory is called a buffer. And a double-buffer uses two areas. One is the current buffer that’s already been drawn to and is what the video card is displaying. The second buffer is the next buffer and once all the drawing of the next frame is done, the buffers can be switched.
You want to make sure that you can switch buffers quickly but even if this takes a little extra time, you’re already much better off. You no longer have the problem of a partially drawn frame appearing to the user.
This pattern applies not just to video games. If you have a desktop application that allows the window to be resized, the normal process is to first erase everything and then draw the contents of the window at the new size. If your program isn’t using double-buffering, the resizing will result in some very noticeable flashes.
And maybe you have a class with many data properties and you want to change several of them one at a time but don’t want some other code to come along and read some of the original properties and some of the newly updated properties.
Whether you need to double-buffer video memory or data properties, this pattern requires you to set aside two copies in memory. You’ll get the fastest performance when you use a couple pointers called current and next to point to each of these memory areas. Anytime you have code that wants the current data, it can use the memory pointed to by the current pointer. And anytime you have code that wants to make changes, it can work with the next pointer and then once all the changes are done, the current and next pointers can be swapped.
You might find some explanations recommend that all reads go through the current pointer while all writes go through the next pointer. Just be careful with this. It might work for you. But it can also cause problems. What do you do when your updating code needs to also read a value before changing it? Your solution might need to be a little more complicated than just a couple pointers if that’s the case.