Double Buffering: Difference between revisions

+Cat: Video, some minor fixes and code/pre block → source block
[unchecked revision][unchecked revision]
(+Cat: Video, some minor fixes and code/pre block → source block)
Line 4:
== What Is It? ==
==== The Buffers ====
Double-buffering possibly sounds harder than it actually is. In theory, you have a buffer A and a buffer B, which are usually respectively called the front-and back-buffer. On single-buffered displays, you draw straight to video memory, so suppose video memory is located at address <code>0xB800</code> (like console video memory is), you just start modifying values starting from this address (as you probably know) to show characters on the screen. If you would do this with double-buffering (which isn't a very good idea with console video memory, by the way), <code>0xB800</code> would represent buffer A (the front-buffer) whilst buffer B is created and allocated by your favourite <code>malloc</code> function (or <code>new[]</code> operator).
 
==== Where To Write To ====
Instead of writing directly to console video memory (at <code>0xB800</code>), you write everything to the address where Buffer B is located. The process of drawing characters is exactly the same as before, except you're now writing to another memory address.
When you're done drawing, you copy the contents of the back buffer (buffer B) to the front buffer (buffer A). This process is commonly called 'swapping the buffers', though swapping isn't interpreted as switching the contents around, but more of in 'switching the order of the buffers'. By 'swapping the buffers', the back buffer is now seen by the user and carries the same pixel values as the front buffer. The back buffer can then be freely modified again until ready to be then swapped again.
 
Line 14:
 
==== Important Note ====
I used console video memory as an example here, as praticallypractically every OS developer knows how to use this, but it is still not recommended to use double-buffering on a console display, as it is practically useless and wastes memory (unless you're using some kind of game loop for your OS). Double-buffering can be very useful when you're building your own GUI though. On console displays, you usually also don't need double-buffering since there are no problems with artifacts.
 
==== Example ====
For example, without double buffering consider a simple game rendering:
 
<code>void run()
<source lang="c">
<code>void run()
{
while(running)
Line 32 ⟶ 34:
draw_characters();
}
}
}</code>
</source>
 
Now imagine if each of those functions drew directly to the frame buffer. The video card and monitor may update just after you have drawn the background, or it may update just after you have cleared the screen. More likely, you're going to end up with a combination of effects with resulting in your screen flickering and seeing through objects.
 
Now imagine if each of these functions drew to a secondary buffer the size of the frame buffer. Each time you modify a pixel it won't be writing directly to the video card. Instead, after <code>draw_characters()</code> you would call:
 
<code>
<source lang="c">
memcpy(framebuffer, backbuffer, width * height * bytesperpixel);
</code>
</source>
so the final image in it's entirety is sent to the frame buffer, not just parts of it.
 
so the final image in it'sits entirety is sent to the frame buffer, not just parts of it.
 
== Advantages ==
Line 47 ⟶ 52:
* The user does not see every pixel modification (so if the same pixel gets modified 5 times, the user will only see the last one). This helps preventing 'flickering' (or screen flashes).
* Double-buffering usually takes care of artifacts you or the user might be experiencing.
* Writing to video memory is only performed once, on the buffer swap, instead of repeatidlyrepeatedly for every pixel of which some might be overlapped later and might be invisible.
 
== Disadvantages ==
Line 53 ⟶ 58:
 
=== Memory requirement ===
You need to create a second buffer with the same size of the vdeovideo memory (and for large displays such as 1280x1024x32 this can be an expensive cost).
 
This is more of an issue on embedded and older systems with limited memory. In the example of 1280x1024x32, this would mean your application or video driver would need to allocate an extra 5 megabytes, which isn't an expensive once off cost in a modern system that has between several hundred megabytes to several gigabytes of memory.
 
Also, virtually every modern desktop computer has a dedicated graphics accelerator card which has it'sits own inbuilt memory. While older graphics cards may only have 8MB, many newer high-end models can have close to several gigabytes. This is more than sufficient to store the back buffer in, as well as being optimized for this purpose. An OS should allow the video driver to allocate and manage the double buffer since each graphics vendor will usually have their own implementation.
 
Note that the buffer doesn't always have to have the same size as the video memory, there are also other ways to do double-buffering, for one you could also have some sort of compression system where a number indicates how many pixels have the same value. Then again, that would have an extra performance cost if not done efficiently. Below you'll find an example of how to create a basic double-buffering system.
 
=== Speed ===
You need to copy the back buffer to the front buffer every time the buffer needs to be swapped (in games with a frame-rate of 30, 30 times each second). So better make sure your '<code>memcpy'</code> and other memory functions are optimized!
 
However, some graphics cards have the ability to specify the address in memory where the buffer used for rendering is stored. If you're redrawing the entire scene each frame (which is often the case when rendering video or 3D), you simply need to swap two pointers (the pointer to the buffer you're drawing to, and the pointer to the buffer being drawn on screen).
Line 72 ⟶ 77:
 
==== Vertical Synchronization ====
Vertical synchronization ([http://en.wikipedia.org/wiki/Vertical_synchronization WikipediaVertical synchronization]), more commonly known as Vv-Syncsync, is when the frame rate is synchronized to match the vertical refresh rate of the screen. This means the double buffer is copied to screen's buffer in a small period between frames known as the [http://en.wikipedia.org/wiki/Vertical_blanking_interval vertical blanking interval]. By updating the screen's buffer between frames, you ensure that only full frames are shown at a time.
 
To aid in synchronizing, virtually all video cards since the first home computers have the capability to enable an interrupt at the beginning of each vertical blanking interval (known as VBLANK).
 
An example of vertical syncronizationsynchronization is as follows:
 
<source lang="cpp">
/* handle VBLANK, called by the interrupt handler */
Line 111 ⟶ 117:
In a system already utilizing double buffering, triple buffering is simple to implement. In most cases it can be added to the video driver without the underlying system requiring any modifications.
 
If loosing a few MB'sMBs of memory is not an issue then triple buffering provides several advantages. First off, there is no need to synchronize frames, so the drawing algorithm can run as often and as fast as it can. Secondly, some real time applications MUST<em>must</em> perform a consistent number of update cycles each second, in which case synchronizing with the refresh rate (which will cause the program to slow down to the speed of the monitor) or skipping frames (the time it takes to execute a single update will not be consistent; some will render frames, some won't) is not an option.
 
==== Something unique? ====
In GUI applications, where multiple programs are rendering to multiple front and back buffers each at their own speed you need to be somewhat creative.
 
EachFor example, a method that will work is: each window could consist own double buffer (or a single buffer that is updated on a Redrawredraw/Paintpaint event), as well as a double buffer for the entire screen. That way each program can draw on it'sits back buffer and send it it'sits own front buffer when it has finished drawing (either swapping pointers or <code>memcpy</code>) as often as it wants (wrapping a lock around the back buffer), then when the GUI has detected the back buffer has changed, it copies the window's front buffer (locking it temporarily) into the screen's back buffer, then when the VBLANK fires the screen's back buffer is copied to the front buffer if the screen's back buffer has changed.
For example, a method that will work is;
Each window could consist own double buffer (or a single buffer that is updated on a Redraw/Paint event), as well as a double buffer for the entire screen. That way each program can draw on it's back buffer and send it it's own front buffer when it has finished drawing (either swapping pointers or memcpy) as often as it wants (wrapping a lock around the back buffer), then when the GUI has detected the back buffer has changed, it copies the window's front buffer (locking it temporarily) into the screen's back buffer, then when the VBLANK fires the screen's back buffer is copied to the front buffer if the screen's back buffer has changed.
 
Each window manager usually has it'sits own method. But like mentioned above, tearing is a relatively minor issue in most general GUI programs that can often be overlooked, since you will be dealing with text and images that won't be updating. Therefore, if the front buffer is in the middle of outputting to the monitor while copying the next frame to it, you might notice a slight flicker that lasts for all but 1 monitor refresh (which is no less than 1/60th of a second on most monitors).
 
== Examples ==
Line 283 ⟶ 288:
</source>
 
I believe this example is mostly clear. As there are too many different ways of changing video modes or putting pixels, I'm going to let you fill that in yourself. If you want more information, you can go to the [[GUI|GUI]] or the [[Drawing_In_Protected_Mode|Drawing In Protected Mode]] pages.
 
Note also that the above code might be very 'irregular'. As you know, there is no limit on the drawing, so it is possibly that the first draw takes 30 milliseconds, while the second draw takes 40 milliseconds or the first few seconds, you have a frame-rate of 100 frames per second whilst the next couple of seconds, you have a frame-rate of only 20 frames per second. Because of this, drawing times are usually capped. In the most common GUI'sGUIs (such as the one in Windows), this is not really of any importance, since only parts of the screen are redrawn if they are 'invalidated' (usually referred to as 'Invalidated rectangles'), but in games, the screen is being constantly redrawn, usually up to 60 times per second. Today's API'sAPIs usually clamp this value to the screen refresh rate, this is why they usually have values such as '60' or '75'. Normally, a frame-rate of 25 to 30 suffices to have a 'smooth' display.
 
I've used some sort of loop here (it looks like a game loop), but remember that most GUI'sGUIs on OS'most OSes do not work with game loops, they usually work with some sort of invalidation system. Meaning a control is redrawn only if it is invalidated, which happens on occassionsoccasions such as moving or resizing the control.
Note also that the above code might be very 'irregular'. As you know, there is no limit on the drawing, so it is possibly that the first draw takes 30 milliseconds, while the second draw takes 40 milliseconds or the first few seconds, you have a frame-rate of 100 frames per second whilst the next couple of seconds, you have a frame-rate of only 20 frames per second. Because of this, drawing times are usually capped. In the most common GUI's (such as the one in Windows), this is not really of any importance, since only parts of the screen are redrawn if they are 'invalidated' (usually referred to as 'Invalidated rectangles'), but in games, the screen is being constantly redrawn, usually up to 60 times per second. Today's API's usually clamp this value to the screen refresh rate, this is why they usually have values such as '60' or '75'. Normally, a frame-rate of 25 to 30 suffices to have a 'smooth' display.
 
[[Category:Video]]
I've used some sort of loop here (it looks like a game loop), but remember that most GUI's on OS' do not work with game loops, they usually work with some sort of invalidation system. Meaning a control is redrawn only if it is invalidated, which happens on occassions such as moving or resizing the control.
57

edits