Wednesday, August 25, 2010

Double Buffering

Start at the Beginning
Table of Contents

My preferred approach for drawing pixels (and thus images) to the screen is something called double buffering. I prefer it because it's simple, it's fast, and because we'd need to implement it eventually anyways (I'll explain why in the next section).

With double buffering, we draw our screen image into a block of memory we handle ourselves, and then we blast it into video memory as quickly as possible. We only need to deal with the vestigal 16-bit memory layout once, when we do that fast copy - but happily, there's a built in function that manages it all for us.

For the record - while I'm writing this book from first principles to try to engender a deep understanding of the concepts, and normally I'm opposed to this sort of "just let the computer do it" handwaving - here I'm interested in teaching game programming concepts, not completely worthless, x86-specific processor tinkering concepts. No one should have to know this anymore unless they're writing an OS. No one should have ever had to know this!

To implement double buffering, first we'll need to create our "back buffer". The back buffer is a chunk of memory exactly like the one in video memory where the VGA stores the screen, except that it's in our address space - meaning it's memory we can access whenever and however we want.

#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240

/* here, we allocate a global array to store our screen buffer. An array is basically a chunk of memory that stores a series of items of the same type. Here, it stores a series of bytes representing the pixels that form the image on-screen. There's a slot for each pixel on the screen, so to compute the total number, we compute SCREEN_WIDTH*SCREEN_HEIGHT */
unsigned char screen_buffer[SCREEN_WIDTH*SCREEN_HEIGHT];


Then we need a way to copy this back buffer into video memory. To do this we'll use a function DJGPP (our compiler) provides called dosmemput(). dosmemput() is non-standard (meaning it'll only work with DJGPP on DOS, not with other compilers or operating systems) function that can copy a large chunk of data to anywhere in memory quickly - even outside of our program's address space, into places like video memory. The call we'll use to actually copy our back buffer will look like this:

dosmemput(screen_buffer, SCREEN_WIDTH*SCREEN_HEIGHT, 0xA0000);

That means we're copying SCREEN_WIDTH*SCREEN_HEIGHT bytes from screen_buffer to 0xA0000 - where the VGA stores the screen. Now we can write to our back buffer as much as we want, quickly and easily. When we're done, we copy the back buffer over to put it on screen:

/* imagine we have some constant RED defined for a red pixel.
This line would make the very top left pixel red */

back_buffer
[0] = RED;
/* This line would make the pixel to the right of it red */
back_buffer
[1] = RED;
/* And this would make the pixel below it red.
Each row has 320 pixels, and the rows are stored top to
bottom, one after the other */

back_buffer
[320] = RED;
/* now copy the back buffer to the screen */
dosmemput
(screen_buffer, SCREEN_WIDTH*SCREEN_HEIGHT, 0xA0000);

Previous

No comments:

Post a Comment