
Game Programming Patterns: Double Buffer (2014) - tosh
http://gameprogrammingpatterns.com/double-buffer.html
======
x1000
I thought this would only apply to rendering graphics, but after reading the
"When To Use It" section, I realized I've done double buffering on entire game
states before (~2 years ago project). At the beginning of my update loop, I'd
(deep) copy the current game state into a new object and begin incrementally
updating the copy. Then I'd reassign right before Thread.sleeping (or whatever
language idiom) until the next game loop "tick".

Wasn't too fond of my C# deep copy solution: var serialized =
JsonSerializer.Serialize(this); return
JsonSerializer.Deserialize<GameState>(serialized);

I took an interested in functional programming, pure functions, immutability,
etc. soon after.

~~~
Sharlin
"Double buffered" (transactionally updated) game state is basically required
if you want to avoid strange nondeterministic glitches as entities update
based on partially updated world state. Persistent immutable data structures
are great for this purpose as they typically attempt to minimize actual
copying. UI logic in regular applications also greatly benefits from immutable
state, which is indeed what things like React are based on.

~~~
Jare
Double buffering and determinism are not necessarily related. Determinism
requires that everything that could affect the execution of logic is captured
as part of the input, and (this is the annoying and error-prone part), that
anything that is not captured as part of the input does NOT affect execution
of logic.

If execution of logic is always performed in the same order, then the
partially updated world state is not a problem for determinism (though it may
be for correctness). Double buffering may let you reorder the execution of
logic in different ways and still keep it deterministic. This would likely be
needed for logic that executes in parallel threads, which is increasingly
important for high-performance game engines.

~~~
Sharlin
Yes, true, ”unpredictable” is probably better. Depending on how exactly entity
lists are managed, from player/dev point of view it can seem close to
nondeterministic in practice even if a theoretical 1:1 replay would give
identical results.

------
dahart
This is a very nice article on double buffering, and I especially love the
extension to game state. I never thought of that at double buffering, but yes
I’m frequently keeping “previous frame” state for a lot of different systems
including physics, controllers, AI state machines, etc. They are usually ad-
hoc buffering. Each system handles it’s own. This makes me wonder if it would
make any sense to unify previous frame memory, what it would look like, and
whether there might be advantages.

> In their hearts, computers are sequential beasts. [...even with several
> cores, only a few operations are running concurrently.]

My one small nitpick is that the presentation at the beginning felt very
single-core CPU centric, even with the side note on threading. GPUs are both
parallel processors, and they’re being used in parallel with the CPU; many
games are running things like CPU physics in parallel while the GPU is doing
rendering. And to really see this point, many games use triple buffering and
are doing some amount of overlapped rendering of two frames at the same time,
not just one.

------
aidenn0
Interestingly enough, there were many games in the 8 bit era that you could
watch the scene render from back to front (golf games were notorious for
this), and going back even further, early video game systems lacked a frame
buffer at all.

~~~
yason
The golf games (Leaderboard Golf comes to mind:
[https://www.youtube.com/watch?v=4jA5eKk10Wo&t=803](https://www.youtube.com/watch?v=4jA5eKk10Wo&t=803))
had to do this because it took incredibly long time to render a 3D map on the
screen. No chance those machines could've rendered the whole scene within a
few frames.

The games just made it a visual gimmick for the player to watch the drawing
instead of offering a waiting screen, rendering to a backbuffer and then
copying the image over.

Frankly, I wouldn't be surprised if the rendering was even made a bit slower
so that the player can see the areas fill up in human-scale time. For example,
a C64 with 6502 can fill polygons faster than what you see on the screen.

~~~
rusk
I don't know ... I remember using a paint program where something like a flood
fill, actually ran before your very eyes... at something like 4-5MHz there
isn't a lot of CPU to do your bidding so game logic, music, graphical
rendering all takes a slice and you don't even have any kind of preemptive
scheduler to make sure everyone stays in time. I'd say with this kind of game
the developers were more focused on the game mechanics. The graphics were just
"good enough" for what they were trying to do.

~~~
Narishma
The C64 ran at 1Mhz. Other contemporary CPUs that were clocked faster tended
to take multiple clock cycles per instruction so it came down to the same.

~~~
rusk
Was it the spectrum that ran at 4MHz? Thanks for the correction!

~~~
Steve44
Not the poster but yes, the Spectrum was at about 3.5Mhz.

------
thebosz
If this article tickles your fancy, you should read the whole book. Bob did a
great job on it! It's all available on the website for free, or you can pay
for a nice copy if you'd like to support his efforts.

He's also writing a new book about creating interpreters:
[https://craftinginterpreters.com/](https://craftinginterpreters.com/)

~~~
munificent
Thank you for the compliment! :)

~~~
trevortheblack
Thank you for the book! The chapter on the prototype pattern was the first
time that I fully grokked the Factory pattern. And, your's is the only
resource I've found that has thoughtfully constructive thoughts on singletons.

------
Neil44
On games this can be quite expensive in terms of frames per second and
smoothness because of how it interacts with the monitors refresh rate. If the
next buffer isn’t quite ready in 1/60th of a second for the start of the
monitors refresh then you basically have to sit there doing nothing until the
monitor is ready for you again. Now the user sees the same image for two
frames then a jump and your frames per second has fallen off a cliff. You can
try to mitigate that with triple buffering but that’s starting to take a lot
of memory up now that you want to use for other things.

AMD has this standard for interacting with the monitor refresh now called
FreeSync. Now the game can communicate with the monitor to tell it when the
buffer’s ready, to get around the problems caused by a fixed 60hz refresh. A
lot of ‘gaming’ branded screens are supporting it now.

~~~
paulmd
(a) this uneven frame-pacing is called "judder"

(b) you only get judder with vsync=on, vsync=off just flips to the new buffer
as soon as it's available. The downside is that you tend to have a visible
seam or "tear" in the image when the buffer flips.

(c) the memory for triple-buffering is trivial, that's not the problem. The
real problem is that you need to be pushing a framerate at least as high your
monitor's refresh rate, and ideally 2x your refresh rate. That's undesirable
in a world where you don't have infinite money to spend on hardware.

(d) NVIDIA actually pioneered this technology, AMD came in with a copycat
implementation after the fact. AMD has the advantage of not needing special
hardware in the monitor, but it usually comes with the disadvantage of only
being able to sync over a narrow range, such as 40-60 Hz. Some monitors have
as narrow as a 10-hz sync range, and many of them tend to flicker once they
get down to the lower end of their sync range. There are currently a total of
three FreeSync monitors on the market that don't totally suck.

[http://techreport.com/news/25867/amd-could-counter-
nvidia-g-...](http://techreport.com/news/25867/amd-could-counter-nvidia-g-
sync-with-simpler-free-sync-tech)

[https://gpunerd.com/guides/freesync-monitor-
list](https://gpunerd.com/guides/freesync-monitor-list)

~~~
Nimelrian
> with the disadvantage of only being able to sync over a narrow range, such
> as 40-60 Hz.

1) This is not the problem of FreeSync itself but of the panels and their
support for VRR.

2) I have 2 FreeSync monitors at home which both do 40-144Hz (Acer XF270HUA
and Benq XL2730Z). In addition, more than 2 years ago AMD added LFC to help
with the classic case of "FPS drops below FreeSync range" issue. I don't
notice any issues with judder when my framerate goes below the range for 1 or
2 frames.

Your info is out of date. Nowadays FreeSync is just as good as GSync if you
get a monitor of good quality (and you still pay 150-300$ less just because it
isn't GSync).

~~~
ByThyGrace
Are FreeSync and GSync interchangeable? Can a FreeSync monitor work with an
NVIDIA card, or viceversa?

~~~
ghusbands
No. When NVidia created GSync, they enforced quality far beyond what the
specification could manage and used proprietary technology and consultant
engineers to accomplish it. AMD copied their efforts with Freesync by getting
some of it added to the DisplayPort protocol. NVidia sees it as an inferior
copy and won't support it. AMD and many others see GSync as an exclusionary
market grab through tie-ins.

Despite many claims to the contrary from Freesync supporters, it's still the
case that only a handful of Freesync displays will give near the same
experience as nearly all GSync displays.

------
kristiandupont
Having grown up with this, I think it's part of the reason why React feels so
natural and "right" to me.

Whenever I've used frameworks based on data-binding, my mind tries to rely on
an analogy like modular synthesis where elements are connected with wires
([https://modularsynthesis.com/DJB-
synth.jpg](https://modularsynthesis.com/DJB-synth.jpg)). That works great for
tiny demos but poorly for highly dynamic UI's. Maybe I just need a different
analogy to properly grasp it.

~~~
sfvisser
Weird comparison. The native DOM already performs some kind of buffering. All
changes to the DOM tree in one single JS thread are rendered at once. React
just has a slightly simplified virtual version of the DOM so it can be smarter
about reuse, possibly causing speedups.

~~~
kristiandupont
I just meant that the virtual DOM feels like a backbuffer that you can render
"from scratch" with every frame. I am well aware that the technicalities are
quite different.

------
gfo
Great read! I'm still missing something though...

Is there synchronization between the GPU and the FrameBuffer to tell it that
the GPU has finished drawing the current frame? This may be a rare case but
I'm getting stuck at the Sample Code.

I'm assuming we're reusing the same FrameBuffer object so it seems like we're
using the same two pixel array references. If the GPU can't tell the game it's
not done with the current buffer, couldn't the game easily start writing into
the buffer the GPU is currently working with if it's just swapping in its own
loop? This assumes the game has already drawn the next frame and has swapped
back to the former frame.

In the reverse case I guess the game loop could fall behind and we keep
writing the same frame and introducing lag.

------
NanoWar
This is also build-in into slower hardware, such as the TI83 calculator. There
is a command to copy the buffer to the actual screen memory, so it updates all
at once :)

------
ijidak
What language is the code sample in? Looks very clean.

~~~
wtetzner
It looks like C++ to me.

~~~
munificent
Yes, I used a subset of C++ for the book. The goal was to be low-enough level
to convince die hard game programmers that the examples were valid but clean
enough for non-C++ programmers to read.

One of the things I find really interesting about C++ is that idiomatic,
modern C++ is _really_ gnarly looking. Lots of `std::` everywhere, tons of
templates, etc.

But if you choose to eschew convention and roll your own stuff, you can
actually write C++ in a pretty simple, clear style. There are significant
downsides to doing that, of course. The prevailing style is prevailing for a
reason. But I think it says something interesting about the language itself
that it's so accommodating to a wide range of styles and levels of
readability.

~~~
ijidak
Wow. Very finely crafted. Didn't even recognize it at first.

Lacks all of the clutter I tend to associate with c++.

