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

Использование сигнала вертикальной синхронизации


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

Нам это и нужно. Период обратного вертикального хода луча — самое подходящее время для обновления видеобуфера, так как в этот момент у аппаратной части нет к нему доступа. Этот период инициируется с помощью сигнала вертикальной синхронизации (vsync), посылаемого картой VGA. Мы должны определить этот импульс и использовать период, следующий сразу за ним, чтобы паша мультипликация в точности синхронизировалась с частотой обновления экрана.

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

К счастью, карты VGA имеют специальный регистр, который отражает обратный ход луча. Он называется регистром состояния VGA и его можно прочесть, обратившись к порту 0x3DA. Из 8 битов, содержащихся в этом регистре, нас интересует четвертый справа бит (3d):

§          Когда бит установлен в 1, происходит обратный вертикальный ход луча;

§          Когда бит сброшен в 0, происходит перерисовка экрана.

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


Итак, мы можем написать подпрограмму, которая будет запрашивать статус бита и отслеживать начало процесса обратного вертикального хода луча. После этого она вернет управление основной программе, которая будет знать, что следующую 1/70-ю долю секунды для VGA (и 1/60-ю секунды для EGA), можно совершенно спокойно писать в видеобуфер.

Я написал демонстрационную программу VSYNC.С, которая выполняет подобную синхронизацию и вычисляет количество периодов обратного вертикального хода луча как функцию от времени. Так как за секунду выполняется 70 циклов регенерации экрана, то после минутной работы программы результат должен оказаться равным 4200. Листинг 7.5. содержит исходный текст этой программы.

Листинг 7.5. Подсчет количества циклов обратного вертикального хода луча (VSYNC.С).

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



#include <dos.h>

#include <bios.h>

#include <stdio.h>

#include <math.h>

#include <conio.h>

#include <graph.h>

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

#define VGA_INPUT_STATUS_1   0x3DA // регистр

состояния

VGA,

// бит 3 - сигнал вертикальной синхронизации

// 1 - происходит обратный вертикальный ход луча

// 0 - нет обратного вертикального хода луча    

#define VGA_VSYNC_MASK 0х08

// маскировка неиспользуемых битов регистра

// ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ ////////////////////

unsigned char far *video_buffer

= (char far

*)0xA0000000L;// указатель на видеопамять

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

void Wait_For_Vsync(void)

{

// эта функция ожидает начала обратного вертикального хода луча, а

// если луч уже совершает обратный ход - ожидает следующего цикла

while (_inp(VGA_INPUT_STATUS_1) & VGA_VSYNC_MASK)

{

// обратный вертикальный ход луча - ничего не делаем

} // конец оператора while

// ожидаем прихода сигнала vsync и возвращаем

// управление вызвавшей функции

while (! (_inp(VGA_INPUT_STATUS_l) & VGA_VSYNC_MASK) )

{

// ожидание начала обратного вертикального



// хода луча - ничего не делаем

} // конец оператора while

// начался обратный вертикальный ход луча –

// возвращаем управление вызвавшей функции

} // конец Wait_For_Vsync

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

void main(void)

{

long nuinber_vsyncs=0;  // хранит количество циклов

// обратного вертикального хода луча

while(!kbhit())

{

// ожидание vsync

Wait_For_Vsync(};

// выводим графику или что-либо еще пока происходит

// обратный вертикальный ход луча и мы имеем в своем

// распоряжении 1/70 секунды! Обычно в это время выполняется

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

// увеличивается количество циклов vsyncs

number_vsyncs++;

// вывод на экран

_settextposition(0,0) ;

printf ("Number of vsync's = %ld   ",number_vsyncs);

} // конец оператора while

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

Существует миллион вещей, для которых можно использовать обратный вертикальный ход луча:                                      

§          Мы можем использовать это время в качестве основы для некоторых процессов или событий;

§          Мы можем использовать это для того, чтобы игровой цикл выполнялся с частотой 70Гц на любой машине;

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

Замечание

Я хотел бы сказать вам еще и о том, чего вы делать не должны. Речь идет об использовании прерывания по обратному вертикальному ходу луча. Персональные ...компьютеры имеют аппаратную поддержку вектора прерывания периода обратного вертикального хода луча. Однако такая поддержка не является стандартной для IBM-совместимых персональных компьютеров и не всегда может работать. К тому же, при этом используется INT2, который зарезервирован IBM для фатальных ошибок аппаратного обеспечения. Поэтому я не буду говорить об этом прерывании, так как оно зависит от марки компьютера и большинство программистов его не используют.

Перейдем к следующей интересной и захватывающей теме: мультипликации с помощью регистров цвета.


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