Какую проблему решает двойная или тройная буферизация в современных играх?

31

Я хочу проверить, правильно ли я понимаю причины использования двойной (или тройной) буферизации:

Монитор с частотой обновления 60 Гц отображает монитор 60 раз в секунду. Если монитор обновляет монитор-дисплей, он обновляет пиксель для пикселя и строку для строки. Монитор запрашивает значения цвета для пикселей из видеопамяти.

Если я сейчас запускаю игру, то эта игра постоянно манипулирует этой видеопамятью.

Если в этой игре не используется буферная стратегия (двойная буферизация и т. Д.), Может возникнуть следующая проблема:

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

Верно ли мое понимание случаев использования буферной стратегии? Есть ли другие причины?

krokvskrok
источник

Ответы:

62

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

Далее я упоминаю «vsync». Vsync - это момент, когда монитор начинает рисовать новое изображение на экране; это точка, в которой «vblank» начинается на традиционном экране ЭЛТ, где линия сканирования на мгновение прекращает рисовать и возвращается к верхней части монитора. Этот момент очень важен для многих подходов к согласованности кадров.

«Разрыв» - это то, что мы называем этим, когда экран визуализируется из двух разных изображений в одном кадре. Если, например, я нарисовал два изображения экрана, которые должны отображаться одно за другим, но монитор вместо этого отобразил верхнюю половину первого кадра и нижнюю половину второго кадра, это «разрыв». Это происходит из-за изменения данных, которые считывает монитор во время рисования, а не во время vblank. (В современных программах это обычно происходит потому, что пользователь отключил ожидание vsync в своих настройках драйвера)

Zero-Buffer

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

Single-Buffer

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

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

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

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

Дважды Buffer

Это намного, намного проще, чем любая из ранее разработанных стратегий.

В системе с двойной буферизацией у нас достаточно памяти для хранения двух разных изображений на экране, поэтому мы обозначаем одно из них как «передний буфер», а другое - «задний буфер». «Передний буфер» - это то, что отображается в данный момент, а «задний буфер» - то, где мы в данный момент рисуем.

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

Triple-буфер

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

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

Монитор отображает передний буфер, и мы рисуем в задний буфер # 1. Если мы закончим рисование в заднем буфере # 1 до того, как монитор завершит рисование переднего буфера, то вместо ожидания vsync мы немедленно начнем рисовать следующий кадр в задний буфер # 2. Если мы закончим, а vsync все еще не пришел, мы начнем рисовать обратно в задний буфер # 1 и так далее. Идея состоит в том, что когда vsync в конечном итоге произойдет, один или другой из наших обратных буферов будет завершен, и этот можно заменить на передний буфер.

Преимущество тройной буферизации состоит в том, что мы не теряем время, потраченное на ожидание vsync в подходе с двойной буферизацией, и изображение, помещенное в передний буфер, может быть «свежее», чем то, которое ожидало vsync для 8ms. Недостатком тройной буферизации является то, что нам нужна дополнительная память для хранения дополнительного изображения экрана, и что наше использование процессора / графического процессора будет выше (опять же, поскольку мы не замедляем ожидание vsync).

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

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

Тревор Пауэлл
источник
7
Это невероятно подробный ответ, объясняющий, почему многие вещи выполняются такими, как они есть (например, экранное координатное пространство и т. Д.). Также ответил на мой вопрос "почему не четырехкратные буферы? Пятикратный?" Я не осознавал, что смена буфера происходила в фоновом режиме, а это означает, что два обратных буфера являются максимально необходимым. Upvoted.
lunchmeat317
На самом деле существует четырехугольная буферизация. Это двойная буферизация для стереоскопического обзора. swiftless.com/tutorials/opengl/smooth_rotation.html
Нарек
@Narek Нет. Цитирую по вашей ссылке: «Вы не можете реально включить фактическую четырехугольную буферизацию в среде компьютерной графики, поскольку в этом нет никакой реальной причины». Предполагать, что двойная буферизация на двух разных видах одновременно будет «четырехъядерной буферизацией», - просто забавная игра слов; не что-то реальное.
Тревор Пауэлл
@TrevorPowell Добавляя часть, которую вы забыли включить: «Ну, обычно четырехугольная буферизация относится к двойной буферизации в стереоскопической среде. Например: у вас есть два дисплея, обычно для целей 3D, и каждый глаз имеет двойную буферизацию». Теперь контекст может быть более понятным.
Нарек
@TrevorPowell Ваша точка зрения совершенно ясно, хотя. Не имеет смысла иметь более 3 буферов для одного буфера рендеринга.
Нарек