Отсебятина
Наткнулся год назад на ряд очень интересных статьей господина Simon. Саймон очень любит разбирать то, как создаются игры, а именно графические решения того или иного элемента в игре. Начиная от сколов на гранях плит, заканчивая тем, как реализовано отрезание кусков от объектов. Но особенно интересным представляется его ряд статей под общим названием «Ад рендера» (Render Hell), в котором он подробно разбирает, как на уровне железа (да и программно тоже) происходит рендер 3D-объектов.
Перевод вольный. Его я делал для себя, чтобы в какой-то момент я мог вернуться и прочитать то, что мог не уловить с первого раза или просто забыть.
Ну что, начнем?
Книга первая. Обзор
(оригинал книги здесь)Ребятушки, держитесь: с точки зрения ПК, ваши работы в 3D — ничто иное, как просто список вертексов и текстур. Все эти данные конвертируются в картинку Некст-гена, и в основном это делается с помощью процессора системы (CPU) и графического процессора (GPU).
Сначала данные загружаются с вашего жесткого диска (HDD) в оперативную память (RAM) для быстрого доступа к ним. После этого нужные для отображения (рендера) Объекты (Meshes/Меши) и текстуры загружаются в оперативную память видеокарты (VRAM). Это связано с тем, что доступ к VRAM у видеокарты намного быстрее.
Если текстура больше не нужна (после выгрузки в VRAM), она может быть удалена из оперативной памяти RAM (Но вы должны быть уверены, что она вам больше не понадобится в ближайшее время, потому что выгрузка с HDD занимает очень большое время).
Меши должны оставаться в RAM, потому что скорей всего процессор захочет иметь доступ к ним, например, для определения столкновения.
Дополнено второй редакцией
Теперь вся информация на видеокарте (в оперативке видеокарты — VRAM). Но скорость перевода из VRAM к GPU все еще низкая. GPU может обработать намного больше информации, чем к нему поступает.
Следовательно, инженеры поместили маленький объем памяти прям в сам видеопроцессор (GPU) и назвали эту память кэш (Cache). Это небольшой объем памяти, потому что это невероятно дорого — помещать большой объем памяти напрямую в процессор. GPU копирует в кеш только то, что ему сейчас необходимо и малыми порциями.
Наша скопированная информация теперь лежит в кэш 2-го уровня (L2 Cache). В основном, это маленький объем памяти (например, на NVDIA GM204 объем составляет 2048 кбайт), который установлен в GPU и доступен для чтения намного быстрее, чем VRAM.
Но даже этого не хватает, чтобы работать эффективно! Поэтому есть еще маленький кеш 1-го уровня (L1 Cache). На NVIDIA GM204 Он составляет 384Кбайт, который доступен не только для GPU, но и ближайших сопроцессоров.
Кроме того, есть еще одна память, которая предназначена для входных и выходных данных для GPU ядер: для регистрации файлов и записи. Отсюда GPU берет, например, два типа значений, считает их и фиксирует результаты в регистр:
После чего эти результаты помещаются обратно в L1/L2/VRAM, чтобы освободить место для новых расчетов. Вы, как программист, обычно не должны беспокоиться на счет их расчетов.
Почему это все работает без проблем? Как сказано выше, это все о времени доступа. И если мы будем сравнивать время доступа, например, HDD и L1 Cache, то между ними черная дыра — такая вот разница. Можно так же почитать о точных цифрах задержки по этой ссылке: gist.github.com/hellerbarde/2843375
Следовательно, инженеры поместили маленький объем памяти прям в сам видеопроцессор (GPU) и назвали эту память кэш (Cache). Это небольшой объем памяти, потому что это невероятно дорого — помещать большой объем памяти напрямую в процессор. GPU копирует в кеш только то, что ему сейчас необходимо и малыми порциями.
Наша скопированная информация теперь лежит в кэш 2-го уровня (L2 Cache). В основном, это маленький объем памяти (например, на NVDIA GM204 объем составляет 2048 кбайт), который установлен в GPU и доступен для чтения намного быстрее, чем VRAM.
Но даже этого не хватает, чтобы работать эффективно! Поэтому есть еще маленький кеш 1-го уровня (L1 Cache). На NVIDIA GM204 Он составляет 384Кбайт, который доступен не только для GPU, но и ближайших сопроцессоров.
Кроме того, есть еще одна память, которая предназначена для входных и выходных данных для GPU ядер: для регистрации файлов и записи. Отсюда GPU берет, например, два типа значений, считает их и фиксирует результаты в регистр:
После чего эти результаты помещаются обратно в L1/L2/VRAM, чтобы освободить место для новых расчетов. Вы, как программист, обычно не должны беспокоиться на счет их расчетов.
Почему это все работает без проблем? Как сказано выше, это все о времени доступа. И если мы будем сравнивать время доступа, например, HDD и L1 Cache, то между ними черная дыра — такая вот разница. Можно так же почитать о точных цифрах задержки по этой ссылке: gist.github.com/hellerbarde/2843375
Прежде, чем рендер начнет зажигать, CPU устанавливает некоторые глобальные значения, которые описывают, как меши должны рендериться. Эти значения называют Render State.
Render State
Это своего рода параметры того, как меши должны рендериться. Параметры содержат в себе инфу о том, какая текстура должна быть, какие вертексные и пиксельные шейдеры должны использоваться для отрисовки последующих мешей, свет, прозрачность и прочее.
И ВАЖНО ПОНИМАТЬ: Каждый меш, который CPU отправляет в GPU для отрисовки, будет рендериться под теми параметрами (Render State), которые были указаны до него. То есть, вы можете отрендерить меч, камень, стул и машину — все они будут рендериться под одной текстурой, если перед каждым из этих объектов не указывать параметры отрисовки RenderState.
Когда все подготовки завершены, CPU может наконец-то позвать GPU и сказать, что нужно рисовать. Эту команду называют Draw Call.
DrawCall
Это команда CPU для GPU отрендерить один меш. Команда указывает конкретный меш для рендера и не содержит в себе никакой информации о материалах и прочего — это все указывается в Render State.
Meш уже загружен в память VRAM.
После того, как команда отправлена, GPU берет RenderState-данные (материал, текстуры, шейдеры), а так же всю информацию о вершинах объекта, и конвертирует эти данные в (нам хочется верить) красивые пиксели на вашем экране. Этот процесс конвертации называется Pipeline (гугл любит переводить это слово, как «трубопровод»).
Pipeline
Как говорилось ранее, любые объекты — это не больше, чем набор вертексов и текстурной информации. Чтобы сконвертировать это в мозговыносящую картинку, видеокарта создает треугольники из вертексов, рассчитывает, как они должны быть освещены, рисует текстуры на них и так далее.
Эти действия называются Pipeline States. Чаще всего большая часть этой работы осуществляется с помощью GPU видеокарты. Но иногда, например, создание треугольников осуществляется с помощью других со-процессоров видеокарты.
Дополнено второй редакцией
Этот пример чрезвычайно упрощен и должен рассматриваться только как приблизительный обзор или «логический» конвейер: каждый треугольник / пиксель проходит логические шаги, но то, что на самом деле происходит, немного отличается от описанного.
Вот пример шагов, которые железо делает для одного треугольника:
Рендер картинки происходит путем решения десятков, сотен тысяч подобных задач, отрисовывания миллионов пикселей на экране. И все это должно (я надеюсь) укладываться как минимум в 30 кадров в секунду.
Современные процессоры имеют по 6-8 ядер, когда как видеопроцессоры имеют несколько тысяч ядер (пусть и не таких мощных, как CPU, но достаточно мощных. чтобы обрабатывать кучку вертексов и прочих данных).
Книга №2 посвящена деталям организации высокого и низкого уровня в графическом процессоре.
Когда информация (например, кучка вертексов) попадает на pipeline, то работу по трансформации из вертексов в полноценное изображение выполняют несколько ядер, поэтому кучка этих элементов формируется в изображение одновременно (параллельно).
Теперь нам известно, что GPU может обрабатывать информацию параллельно. Но что на счет коммуникации между CPU и GPU? Неужели CPU ждет, пока GPU не закончит работу прежде, чем отправить ему новые задачи?
NO!
К счастью, нет! Причиной тому является слабое звено, которое образуется, как горлышко в бутылке, когда CPU не способен отправить следующие задачи достаточно быстро. Решением является лист команд, в которой CPU добавляет команды для GPU, пока тот обрабатывает предыдущую команду. Этот лист называется — Command Buffer.
Command Buffer
Буффер команд делает возможным то, чтобы работы CPU и GPU были независимы от друг друга. Когда CPU хочет что-то рендерить, то запихивает объекты в очередь команд, и когда GPU освобождается — он забирает их из листа (буфера) и начинает выполнять команду. Принцип забора команды — очередность выполнения. Первая команда пришла, первой она и будет выполнена.
Кстати, есть разные команды. Например, одна команда может быть DrawCall, вторая — смена RenderState на новые параметры.
Ну, в общем, это первая книга. Теперь у вас есть представление о том, как информация рендерится, вызываются Draw Calls, Render State, и взаимодействуют между собой CPU и GPU.
The End.
Комментарии (10)
TheMimic
30.08.2017 04:29+1Чёрт возьми, а это действительно интересно! Я думал будет куча графиков, статистики и программных кодов. Ан нет, доступно и понятно. Автор, прошу, продолжай.
Tutanhomon
30.08.2017 09:45«Artists must be strong now» — «Ребятушки, держитесь»? Весьма вольный перевод )
Но все равно, перевод хороший. Только почему статья не помечена как «перевод»?
helg1978
Спасибо за перевод, если будете продолжать — хотелось бы побольше русских слов, мозгу тяжело читать на двух языках сразу: вертекс, рендерится, и т.д.
pipeline это скорей всего конвейер (очередь команд)
MrBrooks Автор
В данном ключе, конвейер не очереди команд, а этапы обработки объектов от идеи на харде до пикселей на мониторе =)
Но мне все еще нравится трубопровод =)
JekaMas
Поддержу, долго думал, что это такое «вертекс», пока не написал по английски и не осознал «вершина»!
Tutanhomon
С одной стороны — да. Но в нашей среде, где общаются художники и программисты, используются именно такие англицизмы (а то и хуже). Так что лично я не имею ничего против такого перевода. Скорее даже «за».
Feelnside
Все верно :) вертексы/трисы/полигоны звучат привычнее.
JekaMas
Тогда все ОК. Разве что для сторонних людей можно было краткое примечание или словарик в начале перевода дать.
MrBrooks Автор
Да, я уже об этом подумал. Когда вторую часть буду выкладывать — добавлю сюда словарик.
YD1
Среды общения бывают разные) В той, в которой учился я, было принято использовать перевод в случае, если он есть и речь идет о понятии, а не о названии. Например: «вершина хранится в буфере вершин», но «там создается vertex buffer», «у нас очень сложный конвейер вывода», но «я добавил пару методов в класс pipeline». Рендер изредка, зачастую в шутку, называли отрисовкой или выводом.