Программирование игр для Windows. Советы профессионала

Дублирующая буферизация


Мы уже вкратце познакомились с понятием дублирующей буферизации в пятой главе, «Секреты VGA-карт». Теперь я хочу познакомить вас с деталями, и на это у меля есть причина.

Дублирующая буферизация используется для уменьшения мерцания экрана, вызванного перерисовкой пикселей. Дублирующий буфер — это область памяти вне видеобуфера, которая используется для построения изображения. Затем, когда изображение построено, и рисунок полностью готов к визуализации, высокоскоростной фрагмент кода перебрасывает его из дублирующего буфера в видеопамять. Обычно для этого используется команда процессора STOSW, которая перемещает несколько слов за один раз. Это наиболее эффективный способ доступа к видеобуферу.

Возможно, вам захочется узнать, работает ли дублирующий буфер медлен­нее чем видеобуфер? Информация, приведенная ниже, поможет вам ответить на этот вопрос:

§

Во-первых, видеопамять работает крайне медленно. Операции записи и считывания осуществляются в 2-10 раз медленнее, чем при работе с оперативной памятью;

§          Во-вторых, построив экранное изображение в дублирующем буфере, мы можем переместить все его 64000 байт за один прием с помощью команды STOSW;

§          Наконец, если мы будем выводить все изображения непосредственно в видеобуфер, то сможем увидеть на экране весь спектр мерцаний и искажений.

Подытоживая все сказанное, можно сделать вывод, что обращение к дублирующему буферу обычно происходит быстрее, чем прямой доступ к видеопамяти, причем и анимация, и графика будут выглядеть на порядок более плавными. Единственный недостаток, это то, что дублирующему буферу требуется 64000 байт памяти, но он того стоит.

С другой стороны, обычно нет необходимости буферизировать весь экран. Если окно игрового пространства имеет только 100 пикселей в высоту, то для  дублирующего буфера потребуется зарезервировать лишь 100 рядов по 320 пикселей, что составит 32К. В четырнадцатой главе в качестве примера игр для нескольких участников, связанных между собой по модему, мы создадим игру, Net-Tank.


В ней мы используем дублирующий буфер только для первых 176 строк дисплея, так как остальную часть экрана будет занимать статичное, один-единственный раз созданное изображение.

Теперь, когда мы знаем, что такое дублирующий буфер и как его использовать, давайте рассмотрим некоторые примеры того,  как он создается и как он действует. Модифицируем и расширим исходные программы двухмерной графики из пятой главы, «Секреты VGA-карт», использовав вместо видеопамяти дублирующий буфер (см. Листинг 7.4). Для этого в каждой функции заменим video_buffer на double_buffer. Программа CIRCLES.С делает следующее:



§          Резервирует память под двойной буфер;

§          Рисует в нем 1000 окружностей;

§          Копирует содержимое буфера на экран за один раз.

Листинг 7.4. Программа CIRCLES.С.

// ВКЛЮЧАЕМЫЕ ФАЙЛЫ ////////////////////////////////////

#include <stdio.h>

#include <math.h>

#include <graph.h>

#include <malloc.h>

#include <memory.h>

#include <string.h>

// ОПРЕДЕЛЕНИЯ //////////////////////////////////

#define SCREEN_WIDTH      (unsigned int)320

#define SCREEN_HEIGHT     (unsigned int}200

// ГЛОБАЛЬНЫЕ

ПЕРЕМЕННЫЕ

//////////////////////////////////////

unsigned char far *video_buffer = (char far *)0xA0000000L;// указатель

на видеобуфер

unsigned char far *double_buffer = NULL;

// ФУНКЦИИ /////////////////////////////////////////////////////////////

void Init_Double_Buffer(void)

{

double_buffer=(char far *)_fmalloc(SCREEN_WIDTH*SCREEN_HEIGHT+1) ;

_fmemset(double_buffer, 0, SCREEK_WIDTH*SCREEN_HEIGHT+l) ;

} // конец Init_Double_Buffer

/////////////////////////////////////////////////////////////

void Show_Double_Buffer(char far *buffer)

{

// копирование дублирующего буфера в видеобуфер

asm {

push ds                // сохранение регистра сегмента данных

les di, video_buffer   // вывод в видеобуфер...



lds si, buffer         // ...из дублирующего буфера

mov сх,320*200/2       // количество перемещаемых слов

cld

rep movsw              // перемещение

pop ds                 // восстановление регистра сегмента данных

}

} // конец Show_Double_Buffer

////////////////////////////////////////////////////////////

void Plot_Pixel_Fast_D(int x,int y,unsigned char color)

{

// вывод,пикселей в дублирующий буфер                  

// используется тот факт, что 320*у = 256*у + 64*у = y<<8 + у<<6

double_buffer[((у<<8) + (у<<6)) + х] = color;

} // конец Plot_Pixel_Fast_D                                        

////////////////////////////////////////////////////////////

void Circles(void)

{

// Эта функция рисует 1000 окружностей в дублирующем буфере

// В реальной игре мы никогда не стали бы использовать столь

// нелепый алгоритм рисования окружности, а применили бы что-нибудь

// вроде таблицы выбора или другой эффективный способ. Здесь же нам

// просто надо хоть что-то нарисовать в дублирующем буфере.       

int index,xo,yo,radius,x,у,color,ang;

// рисуем 1000 окружностей в случайной позиции,

// случайного цвета и размера

for (index=0; index<1000; index++)

{                                                                                                                   

// получаем параметры для следующей окружности

хо     = 20 + rand()%300;

уо     = 20 + rand()%180;

radius = 1 + rand()%20;

color  = rand()%256i

for (ang=0; ang<360; ang++)

{

x = хо + cos(ang*3.14/180} * radius;

у = уо + sin(ang*3.14/180} * radius;

Plot_Pixel_Fast_D(x,y,(unsigned char)color};

} // конец внутреннего цикла

} // конец внешнего цикла

} // конец Circles   

// ОСНОВНАЯ ПРОГРАММА //////////////////////////////////////

void main(void) {

// установка видеорежима 320х200х256

_setvideomode(_MRES256COLOR) ;

// создание дублирующего буфера и очистка его

Init_Double_Buffer() ;

_settextposition (0, 0) ;

printf("Drawing 1000 circles to double buffer. \nPlease wait...");



// построение окружностей в дублирующем буфере

Circles () ;

printf("Done, press any key.");

//ожидание нажатия клавиши, прежде,чем перебросить

// окружности на экран

getch();

ShowDoubleBuffer(double_buffer);

_settextposition(0,0) ;

printf(" That was quick. Hit any key to exit.");

// ожидание нажатия клавиши

getch ();

// восстановление первоначального видеорежима

_setvideomode(_DEFAULTMODE);

} // конец функции main

Конечно, эта программа не самая захватывающая в мире, но она дает четкое представление о том, что нас интересует. Обратите внимание, что функция вывода пикселя записывает его вместо видеобуфера в дублирующий буфер.

Использование дублирующего буфера практически убирает мерцание экрана. Однако мы не можем гарантировать, что копирование данных в видеобуфер не начнется в момент обращения аппаратуры к экранной области памяти, и в этом случае на экране могут возникнуть искажения. В следующем разделе обсуждается то, как этого можно избежать.


Содержание раздела