В предыдущем посте я рассказал о Brew Game Tools (или BGT) — моем 2D-фреймворке для быстрого прототипирования (или, может быть, даже полноценных игр!). Способ рендеринга в BGT заключается в переносе спрайтов в фреймбуфер, поддерживаемый текстурой OpenGL. После того, как все спрайты будут перенесены, текстура применяется к четырехугольнику и отображается. Это довольно простой и гибкий способ создания 2D-рендеринга с помощью программного обеспечения. Однако, в зависимости от количества изображений и разрешения, производительность может сильно различаться! Кроме того, копирование изображений, поддерживающих преобразование (т. е. перемещение, вращение и масштабирование), приводит к дополнительным накладным расходам. В рендерере с аппаратным ускорением преобразование выполняется графическим процессором. Но в нашем случае мы должны сделать это на процессоре. Подробности работы блитинга изображений в BGT выходят за рамки этого рассказа, но я постараюсь написать что-нибудь в ближайшее время!

Сказав это, мы хотели бы получить все ускорения, которые мы можем получить от процессора. К счастью, большинство современных процессоров имеют несколько физических ядер. Популярный способ повысить скорость — разделить задачу рендеринга на несколько потоков на основе 4 квадрантов экрана. У нас будет по одному потоку для каждого из четырех квадрантов экрана. Поток будет отображать только спрайт (или часть спрайта), который попадает в этот квадрант.

Мы создадим один поток и одну очередь команд рендеринга (не показана на рисунке) для каждого из четырех квадрантов экрана. Каждый поток будет отвечать за рендеринг в связанный с ним квадрант.

Теперь в каждом кадре будет происходить следующее:

  1. Когда мы хотим нарисовать спрайт, мы не будем рендерить его сразу. Вместо этого мы сначала выясним, в какой квадрант экрана попадают границы спрайта.
  2. Далее мы создадим команду рендеринга и поместим ее в очередь квадранта. Команда рендеринга — это в основном описание того, что рисовать, где рисовать и как рисовать. Мы можем сохранить команду рендеринга в памяти (в нашем случае в очереди в памяти) и выполнить ее позже.
  3. 4 потока будут продолжать опрашивать очередь для любой доступной команды рендеринга. Каждый раз, когда команда находится в очереди, связанный с ней поток извлечет ее из очереди и выполнит (т. е. отрисует связанный спрайт способом, описанным в команде рендеринга).
  4. Наш основной поток будет ждать, пока все потоки закончат выполнение команд рендеринга в своих очередях. Когда все очереди пусты и все спрайты отрендерены в фреймбуфер, фреймбуфер будет отрисован на экране.

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

Надеюсь, это было несколько познавательно. До следующего раза!