Дублирующая буферизация
Мы уже вкратце познакомились с понятием дублирующей буферизации в пятой главе, «Секреты 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
Конечно, эта программа не самая захватывающая в мире, но она дает четкое представление о том, что нас интересует. Обратите внимание, что функция вывода пикселя записывает его вместо видеобуфера в дублирующий буфер.
Использование дублирующего буфера практически убирает мерцание экрана. Однако мы не можем гарантировать, что копирование данных в видеобуфер не начнется в момент обращения аппаратуры к экранной области памяти, и в этом случае на экране могут возникнуть искажения. В следующем разделе обсуждается то, как этого можно избежать.