Последнее время стараюсь если и прокрастинировать, то все равно делать что-то полезное
Например, вместо диссертации пишу многострадальный гайд по графике на wgpu
Все равно ощущение, что займет это годы, но так хоть какой-то прогресс будет
А если летом защищусь и решу вопрос с призывом, то еще больше времени появится
Например, вместо диссертации пишу многострадальный гайд по графике на wgpu
Все равно ощущение, что займет это годы, но так хоть какой-то прогресс будет
А если летом защищусь и решу вопрос с призывом, то еще больше времени появится
❤10
Дописал вторую главу туториала, вроде бы
Постоянно нахожу, что еще можно дописать или отредактировать, но пока вроде всё устраивает
Вышло 3954 слова без учета блоков кода или 4627 с ними (диаграммы тоже считаются за блоки кода, поскольку они в формате Mermaid)
Надеюсь, что следующие главы будут поменьше, но как будто ощущение, что нет)
Такими темпами, может быть закончу базовый раздел в этом десятилетии)
Постоянно нахожу, что еще можно дописать или отредактировать, но пока вроде всё устраивает
Вышло 3954 слова без учета блоков кода или 4627 с ними (диаграммы тоже считаются за блоки кода, поскольку они в формате Mermaid)
Надеюсь, что следующие главы будут поменьше, но как будто ощущение, что нет)
Такими темпами, может быть закончу базовый раздел в этом десятилетии)
👍6🔥3
откопал старое сообщение про типы рендера, решил сюда закинуть для истории
1. Forward render, когда все просто рисуется напрямую (если вы "просто рендерите", то, скорее всего, у вас именно он).
Конечно, в современных движках уже используются модифицированные алгоритмы (Tiled Forward, Clustered Forward, Forward+ и тд), но общий принцип плюс-минус один.
Из плюсов:
- легко работать с прозрачностью
- поддерживает msaa
- имеет низкие требования к пропускной способности видеопамяти
Из минусов:
- плохо масштабируется с увеличением количества источников света в сцене. В целом больше зависит от сложности сцены, чем от размеров экрана.
- все современные продвинутые реализации требуют компьют шейдеров.
- методы постобработки все равно иногда требуют данные, которых нет в форварде, и приходится мучаться с z препассами и прочим, по сути вводя куски деферред подхода для части данных (например гораздо тяжелее SSAO и SSR реализовать, чем в деферреде).
- плохо переживает мелкие треугольники на экране
1. Forward render, когда все просто рисуется напрямую (если вы "просто рендерите", то, скорее всего, у вас именно он).
Конечно, в современных движках уже используются модифицированные алгоритмы (Tiled Forward, Clustered Forward, Forward+ и тд), но общий принцип плюс-минус один.
Из плюсов:
- легко работать с прозрачностью
- поддерживает msaa
- имеет низкие требования к пропускной способности видеопамяти
Из минусов:
- плохо масштабируется с увеличением количества источников света в сцене. В целом больше зависит от сложности сцены, чем от размеров экрана.
- все современные продвинутые реализации требуют компьют шейдеров.
- методы постобработки все равно иногда требуют данные, которых нет в форварде, и приходится мучаться с z препассами и прочим, по сути вводя куски деферред подхода для части данных (например гораздо тяжелее SSAO и SSR реализовать, чем в деферреде).
- плохо переживает мелкие треугольники на экране
🔥2👍1
2. Deferred render, когда всё рисуется в два этапа - сначала заполняем специальный g-буфер данными, потом на базе него уже рисуем саму сцену. Именно этот способ чаще всего используется в современных игровых движках, из-за простоты и преимуществ
Из плюсов:
- про него очень много информации
- работает даже на очень древнем железе
- масштабируемость зависит больше от размера экрана, а не сложности сцены.
- легче переживает мелкие треугольники на экране.
- постобработке нужны данные из g-буфера, а тут они уже и так есть, то есть легче всякие красивости наводить, включая PBR.
И в целом основная фишка деферреда - ему почти плевать на количество источников света, очень хорошо масштабируется в этом плане, можно тысячи лампочек крутить вокруг объекта с адекватной производительностью
из минусов:
- msaa не работает, сглаживание тут в принципе тяжело сделать нормально.
- прозрачность тоже очень тяжело реализовать, настолько, что часто делают отдельный форвард проход для прозрачных объектов.
- в среднем по больнице требует больше пропускную способность видеопамяти, хотя есть более заморочные современные реализации, которые это потребление уменьшают
Из плюсов:
- про него очень много информации
- работает даже на очень древнем железе
- масштабируемость зависит больше от размера экрана, а не сложности сцены.
- легче переживает мелкие треугольники на экране.
- постобработке нужны данные из g-буфера, а тут они уже и так есть, то есть легче всякие красивости наводить, включая PBR.
И в целом основная фишка деферреда - ему почти плевать на количество источников света, очень хорошо масштабируется в этом плане, можно тысячи лампочек крутить вокруг объекта с адекватной производительностью
из минусов:
- msaa не работает, сглаживание тут в принципе тяжело сделать нормально.
- прозрачность тоже очень тяжело реализовать, настолько, что часто делают отдельный форвард проход для прозрачных объектов.
- в среднем по больнице требует больше пропускную способность видеопамяти, хотя есть более заморочные современные реализации, которые это потребление уменьшают
👍2🔥2
3. Всякие гибридные подходы, когда например делают а-ля деферред, но заполняют не весь g-буфер, а только какую-то небольшую часть данных, необходимую для постобработки. И дальше рендерят как будто форвардом. Или наоборот, делают в целом деферред, но для прозрачных предметов отдельный форвард пасс. В общем, различные комбинации этих подходов для компенсации плюсов и минусов друг друга
например, Doom 2016 сделан на гибридном рендере. У них сначала маленький пре-пасс для заполнения куска g-буфера, а потом используют его + данные рендера прошлого кадра для рендера текущего, из-за чего отражения отстают на один кадр от самой сцены
например, Doom 2016 сделан на гибридном рендере. У них сначала маленький пре-пасс для заполнения куска g-буфера, а потом используют его + данные рендера прошлого кадра для рендера текущего, из-за чего отражения отстают на один кадр от самой сцены
🔥2👍1
4. Новый подход через буфер видимости.
Он чем-то похож на деферред, но вместо рендера текстур в g-буфер, там заполняется числовой буфер с идентификаторами объектов и их треугольников, что позволяет их очень эффективно упаковать, например, в u32. Вообще про это есть доклад от Интела, но уже есть и реализации в разных самописных движках, например цикл из 4 статей про реализацию на DirectX 12 (не самую оптимальную кстати). И в целом есть еще разные варианты этого алгоритма, от разных авторов в разных статьях/докладах
из плюсов:
- работает даже быстрее деферреда, и очень хорошо масштабируется.
- если у форварда запуски фрагментного шейдера зависят от количества источников света, а у деферреда от g-буфера, то тут все привязано к пикселям на экране, и для многих вещей будет только один запуск на один пиксель
- очень нетребователен к пропускной способности видеопамяти, если реализовывать "чистый" буфер видимости, а не использовать его для заполнения g буфера и последующего деферреда
из минусов:
- гораздо сложнее реализовать, чем все предыдущие подходы. Тут и куча производных, и барицентрические координаты, и еще всякое алгоритмическое, графы, и прочее
Он чем-то похож на деферред, но вместо рендера текстур в g-буфер, там заполняется числовой буфер с идентификаторами объектов и их треугольников, что позволяет их очень эффективно упаковать, например, в u32. Вообще про это есть доклад от Интела, но уже есть и реализации в разных самописных движках, например цикл из 4 статей про реализацию на DirectX 12 (не самую оптимальную кстати). И в целом есть еще разные варианты этого алгоритма, от разных авторов в разных статьях/докладах
из плюсов:
- работает даже быстрее деферреда, и очень хорошо масштабируется.
- если у форварда запуски фрагментного шейдера зависят от количества источников света, а у деферреда от g-буфера, то тут все привязано к пикселям на экране, и для многих вещей будет только один запуск на один пиксель
- очень нетребователен к пропускной способности видеопамяти, если реализовывать "чистый" буфер видимости, а не использовать его для заполнения g буфера и последующего деферреда
из минусов:
- гораздо сложнее реализовать, чем все предыдущие подходы. Тут и куча производных, и барицентрические координаты, и еще всякое алгоритмическое, графы, и прочее
👍2🔥2
Тот самый доклад Интела про Visiblity Buffer
и упомянутая серия статей про реализацию на DirectX 12
она не совсем оптимальна, потому что, как и в Unreal Engine, автор использует visibility buffer только для наполнения g буфера, который потом идет в обычный деферред пайплайн. А это, по сути, обнуляет половину преимуществ visibility buffer по сравнению с использованием только его вместо деферреда, таких как низкая зависимость от пропускной способности видеопамяти
http://filmicworlds.com/blog/visibility-buffer-rendering-with-material-graphs/
и упомянутая серия статей про реализацию на DirectX 12
она не совсем оптимальна, потому что, как и в Unreal Engine, автор использует visibility buffer только для наполнения g буфера, который потом идет в обычный деферред пайплайн. А это, по сути, обнуляет половину преимуществ visibility buffer по сравнению с использованием только его вместо деферреда, таких как низкая зависимость от пропускной способности видеопамяти
http://filmicworlds.com/blog/visibility-buffer-rendering-with-material-graphs/
Filmic Worlds
Visibility Buffer Rendering with Material Graphs
👍4
в целом, Nanite из Unreal Engine является мутацией подхода c Visibility Buffer, но он не очень эффективен.
Потому что при использовании visibility buffer нам вообще не нужен g-буфер, мы можем напрямую работать с идентификаторами в компьютах и очень сильно всё ускорить.
А Nanite использует буфер видимости исключительно чтобы ускорить заполнение g-буфера, который потом используется уже у них в обычном деферред пайплайне.
В общем, они сделали более простую реализацию, которая лучше интегрируется в существующий код движка, но далеко не самая оптимальная при этом
Потому что при использовании visibility buffer нам вообще не нужен g-буфер, мы можем напрямую работать с идентификаторами в компьютах и очень сильно всё ускорить.
А Nanite использует буфер видимости исключительно чтобы ускорить заполнение g-буфера, который потом используется уже у них в обычном деферред пайплайне.
В общем, они сделали более простую реализацию, которая лучше интегрируется в существующий код движка, но далеко не самая оптимальная при этом
👍3
собственно, почти все продвинутые варианты этих подходов довольно заморочные, но или дают хороший буст производительности, или решают какие-то проблемы
и почти все эти продвинутые варианты требуют компьют шейдеров, то есть кроссплатформерный опенгл в пролете
и почти все эти продвинутые варианты требуют компьют шейдеров, то есть кроссплатформерный опенгл в пролете
👍2
если подытожить, то есть простая памятка
если у тебя больше стилизованные или низкодетализированные сцены, с малым количеством источников света
то лучше брать форвард рендер, желательно его более современные вариации
это выйдет даже выгоднее по видеопамяти относительно деферреда
если тебе важнее более реалистичная графика, с PBR, кучей эффектов постобработки и тд
или очень много источников света
то лучше брать деферред рендер, потому что тут мы уже значительно выиграем на нагрузке видеокарты относительно форварда
буфер видимости потенциально лучше, но очень заморочный и по нему меньше информации, поэтому для инди вряд ли стоит того
если у тебя больше стилизованные или низкодетализированные сцены, с малым количеством источников света
то лучше брать форвард рендер, желательно его более современные вариации
это выйдет даже выгоднее по видеопамяти относительно деферреда
если тебе важнее более реалистичная графика, с PBR, кучей эффектов постобработки и тд
или очень много источников света
то лучше брать деферред рендер, потому что тут мы уже значительно выиграем на нагрузке видеокарты относительно форварда
буфер видимости потенциально лучше, но очень заморочный и по нему меньше информации, поэтому для инди вряд ли стоит того
👍7
и еще небольшой нюанс - на мобильных устройствах и маках (где по сути та же мобильная архитектура у чипов) само железо больше заточено на деферред рендеринг
следовательно, там он показывает даже лучшие результаты, чем на обычных пк
также, люди, пробовавшие разные подходы, пишут, что в среднем деферред оказывается быстрее даже форвард+, если нужен хоть какой-то постпроцессинг вроде SSAO
https://www.reddit.com/r/GraphicsProgramming/comments/1kmrura/deferred_rendering_vs_forward_rendering_in_aaa/
следовательно, там он показывает даже лучшие результаты, чем на обычных пк
также, люди, пробовавшие разные подходы, пишут, что в среднем деферред оказывается быстрее даже форвард+, если нужен хоть какой-то постпроцессинг вроде SSAO
https://www.reddit.com/r/GraphicsProgramming/comments/1kmrura/deferred_rendering_vs_forward_rendering_in_aaa/
👍2
Немного подробнее про деферред рендер, так как считаю его сейчас оптимальным выбором для инди
В наивном варианте он сводится к рендеру в 2 прохода:
1. Через MRT (Multiple Render Targets) делаем обычную отрисовку наших мешей, но вместо вывода на экран пишем их параметры в отдельные текстуры. Обычный набор - позиции, нормали, альбедо (цвет) и спекулар (отражаемость). Потом можно расширять под PBR другими текстурами по необходимости
полученные текстуры называются G Buffer, и уже содержат только итоговые фрагменты, которые попадут на экран. Они уже прошли тест глубины и прочее, выдав нам итоговые пиксели, просто в нужном формате
а далее просто делается второй прогон, полностью отвязанный от геометрии исходной сцены. Рисуем полноэкранный квад, вертекс шейдер примитивен. А во фрагментном уже считаем свет на базе текстур из G Buffer так же, как считали бы в форварде
Если в наивном форвард рендере нам надо считать свет для каждого фрагмента объекта, даже если он в итоге не будет освещен, и делать это для каждого источника света
То в деферреде на итоговом прогоне освещения мы идем чисто по пикселям экрана и семплим текстуры G Buffer для расчета света.
Отсюда независимость сложности расчета света в деферреде от сложности сцены - сама сцена посчитана на построении G Buffer, для света мы работаем только с итоговыми пикселями
Но тут и минусы - видеопамять и пропускная способность видеокарты тратятся на хранение G Buffer и взаимодействие с ним. А также полупрозрачность требует отдельного форвард прогона. И не работает MSAA, приходится реализовывать более сложные способы сглаживания
но, по сути, почти любой постпроцессинг потом использует те же самые данные из G Buffer, и в конечном счете получается очень выгодно
В наивном варианте он сводится к рендеру в 2 прохода:
1. Через MRT (Multiple Render Targets) делаем обычную отрисовку наших мешей, но вместо вывода на экран пишем их параметры в отдельные текстуры. Обычный набор - позиции, нормали, альбедо (цвет) и спекулар (отражаемость). Потом можно расширять под PBR другими текстурами по необходимости
полученные текстуры называются G Buffer, и уже содержат только итоговые фрагменты, которые попадут на экран. Они уже прошли тест глубины и прочее, выдав нам итоговые пиксели, просто в нужном формате
а далее просто делается второй прогон, полностью отвязанный от геометрии исходной сцены. Рисуем полноэкранный квад, вертекс шейдер примитивен. А во фрагментном уже считаем свет на базе текстур из G Buffer так же, как считали бы в форварде
Если в наивном форвард рендере нам надо считать свет для каждого фрагмента объекта, даже если он в итоге не будет освещен, и делать это для каждого источника света
То в деферреде на итоговом прогоне освещения мы идем чисто по пикселям экрана и семплим текстуры G Buffer для расчета света.
Отсюда независимость сложности расчета света в деферреде от сложности сцены - сама сцена посчитана на построении G Buffer, для света мы работаем только с итоговыми пикселями
Но тут и минусы - видеопамять и пропускная способность видеокарты тратятся на хранение G Buffer и взаимодействие с ним. А также полупрозрачность требует отдельного форвард прогона. И не работает MSAA, приходится реализовывать более сложные способы сглаживания
но, по сути, почти любой постпроцессинг потом использует те же самые данные из G Buffer, и в конечном счете получается очень выгодно
🔥4
Однако, указанная выше реализация все еще очень неоптимальна, потому что даже в ней, мы вынуждены во фрагментном шейдере прохода освещения рассчитывать свет для pixels * number of lights, то есть каждый источник света участвует в расчетах каждого пикселя, даже если физически в сцене объект на нем слишком далеко и не может быть засвечен. Что все равно дорого, пусть и гораздо лучше обычного форварда
есть несколько дальнейших оптимизаций
1. Light volumes. Старый прием, хорош отсутствием необходимости использовать компьют шейдеры, и на этом все
Сводится к тому, что мы заранее для каждого источника света рассчитываем освещаемое им пространство (тот самый "объем"). Например, сферу
И далее, когда у нас идет проход расчета освещения, мы вместо полноэкранного квада просто рисуем эти объемы (сферы, конусы и тд). И считаем свет только в их пределах, Таким образом, мы отсекаем из расчетов источники света, которые находятся слишком далеко
У данного подхода есть ряд минусов:
- нужно рендерить по объекту на источник света, то есть большой overdraw и плохо масштабируется на большое количество источников
- сложности с глубиной, камерой внутри освещенных объемов и т.д.
2. Deferred Lighting (оно же Light Pre-Pass). Тоже старая техника
Делаем три прохода вместо двух - в первый проход (геометрии) заполняем всё, кроме альбедо. Второй считает освещение без знания альбедо и складывает в отдельный буфер. И третий, итоговый, снова рендерит геометрию, читает альбедо и умножает на посчитанный до этого свет
Можно совмещать с light volumes
Суть в том, что такие манипуляции позволяют уменьшить G Buffer, а значит сэкономить видеопамять и пропускную способность видеокарты. Но, при этом, мы вынуждены делать больше проходов, сложнее считать тени, и без полноценных данных в G Buffer сложнее сделать PBR и постпроцессинг
На данный момент встречается редко
3. Tiled Deferred Shading. Более современное решение, требует компьют шейдеров
Идея в следующем: в компьют шейдере делим экран на тайлы (обычно 8х8 или 16х16). Для каждого тайла строим список источников света, пересекающих его область видимости (tile frustum). Это может быть просто список индексов из глобального массива источников света
Далее считаем свет по пикселям тайла, учитывая только его источники освещения. Ну и пишем результат в буфер освещения
Это уже довольно оптимизированный вариант, способный рендерить около тысячи источников света за менее чем миллисекунду
Прикреплю ниже презентацию этого подхода с бенчмарками с SIGGRAPH 2010
Из минусов же:
- большое использование видеопамяти на все эти буферы индексов тайлов, причем оно зависит и от разрешения.
- 2д тайлы не знают глубины. При большом динамическом диапазоне по Z в одном тайле может оказаться много источников света
4. Clustered Deferred Shading. Современный, очень оптимизированный, но довольно сложный в реализации вариант
Это развитие идеи Tiled Deferred Shading.
Вместо 2д тайлов мы разбиваем в 3д - каждый frustum еще нарезается логарифмически по Z, таким образом получаем кластеры.
Аналогично, строим список источников света, но уже для каждого кластера отдельно. Соответственно, при расчете освещения, каждый кластер учитывает только свои источники света, что решает вопрос глубины
Тут довольно много оптимизаций, например можно делать куллинг кластеров по октодереву
Также эти списки источников света для кластеров можно потом переиспользовать, например в отдельном прогоне для прозрачности
Реализация довольно трудоемка, однако выдает очень внушительные результаты - в изначальном докладе их реализация куллила 1048576 случайно распределенных источников света за 6 миллисекунд, в то время как Tiled Deferred Shading справился с той же задачей за 342 миллисекунды
Однако стоит заметить, что в том же докладе Clustered вариант показал себя незначительно хуже Tiled при малом количестве источников освещения (тысяча и меньше), потому что накладные расходы на алгоритм Clustered при таких масштабах проигрывают грубому перебору Tiled
Сам доклад тоже скину
есть несколько дальнейших оптимизаций
1. Light volumes. Старый прием, хорош отсутствием необходимости использовать компьют шейдеры, и на этом все
Сводится к тому, что мы заранее для каждого источника света рассчитываем освещаемое им пространство (тот самый "объем"). Например, сферу
И далее, когда у нас идет проход расчета освещения, мы вместо полноэкранного квада просто рисуем эти объемы (сферы, конусы и тд). И считаем свет только в их пределах, Таким образом, мы отсекаем из расчетов источники света, которые находятся слишком далеко
У данного подхода есть ряд минусов:
- нужно рендерить по объекту на источник света, то есть большой overdraw и плохо масштабируется на большое количество источников
- сложности с глубиной, камерой внутри освещенных объемов и т.д.
2. Deferred Lighting (оно же Light Pre-Pass). Тоже старая техника
Делаем три прохода вместо двух - в первый проход (геометрии) заполняем всё, кроме альбедо. Второй считает освещение без знания альбедо и складывает в отдельный буфер. И третий, итоговый, снова рендерит геометрию, читает альбедо и умножает на посчитанный до этого свет
Можно совмещать с light volumes
Суть в том, что такие манипуляции позволяют уменьшить G Buffer, а значит сэкономить видеопамять и пропускную способность видеокарты. Но, при этом, мы вынуждены делать больше проходов, сложнее считать тени, и без полноценных данных в G Buffer сложнее сделать PBR и постпроцессинг
На данный момент встречается редко
3. Tiled Deferred Shading. Более современное решение, требует компьют шейдеров
Идея в следующем: в компьют шейдере делим экран на тайлы (обычно 8х8 или 16х16). Для каждого тайла строим список источников света, пересекающих его область видимости (tile frustum). Это может быть просто список индексов из глобального массива источников света
Далее считаем свет по пикселям тайла, учитывая только его источники освещения. Ну и пишем результат в буфер освещения
Это уже довольно оптимизированный вариант, способный рендерить около тысячи источников света за менее чем миллисекунду
Прикреплю ниже презентацию этого подхода с бенчмарками с SIGGRAPH 2010
Из минусов же:
- большое использование видеопамяти на все эти буферы индексов тайлов, причем оно зависит и от разрешения.
- 2д тайлы не знают глубины. При большом динамическом диапазоне по Z в одном тайле может оказаться много источников света
4. Clustered Deferred Shading. Современный, очень оптимизированный, но довольно сложный в реализации вариант
Это развитие идеи Tiled Deferred Shading.
Вместо 2д тайлов мы разбиваем в 3д - каждый frustum еще нарезается логарифмически по Z, таким образом получаем кластеры.
Аналогично, строим список источников света, но уже для каждого кластера отдельно. Соответственно, при расчете освещения, каждый кластер учитывает только свои источники света, что решает вопрос глубины
Тут довольно много оптимизаций, например можно делать куллинг кластеров по октодереву
Также эти списки источников света для кластеров можно потом переиспользовать, например в отдельном прогоне для прозрачности
Реализация довольно трудоемка, однако выдает очень внушительные результаты - в изначальном докладе их реализация куллила 1048576 случайно распределенных источников света за 6 миллисекунд, в то время как Tiled Deferred Shading справился с той же задачей за 342 миллисекунды
Однако стоит заметить, что в том же докладе Clustered вариант показал себя незначительно хуже Tiled при малом количестве источников освещения (тысяча и меньше), потому что накладные расходы на алгоритм Clustered при таких масштабах проигрывают грубому перебору Tiled
Сам доклад тоже скину
🔥4
EVE Online всегда занимала у меня особое место в сердце
Не зря наиграл в нее больше 10к часов в стиме, и еще немало вне стима. И пусть сейчас играть уже некогда, и зачастую интереснее писать код в свободное время, многие аспекты игры до сих пор восхищают. Например, реализация сервер меша на питон бекенде в 2003 году, с которой до сих пор, более 20 лет спустя, смогла сравниться лишь пара игр
Другой из этих аспектов - как разработчики год за годом обновляли графику игры, при этом сохраняя отличную оптимизацию и очень низкие системные требования. Игра регулярно выглядела лучше и лучше, при этом все еще оставаясь играбельной даже на встроенной видеокарте бюджетного ноутбука
Сейчас, к сожалению, авторы игры уже не очень охотно делятся техническими подробностями ее работы. Но так было не всегда, и можно найти старые подробные описания, как все было устроено под капотом
Вот один из примеров
https://www.eveonline.com/news/view/the-many-additions-of-v5plusplus
Статья от 2015 года, где рассказывают про новые оптимизации их доработанного PBR. Тут и увеличение допустимого количества материалов на одну модель, и новые способы упаковки текстур, и смена расчета модели отражений
Редизайны сайта не пощадили статью, и некоторые ссылки в ней теперь отображаются как обычный текст. Но при ручном открытии они все еще работают, и можно наглядно посмотреть, о чем же шла речь
В целом, я считаю, что у этих ребят можно многому научиться в техническом плане. Особенно в разрезе доработки визуала при сохранении отличной оптимизации
В конце концов, они смогли перейти от ужасно устаревшей графики к современной, не растеряв поддержки бюджетных устройств)
Не зря наиграл в нее больше 10к часов в стиме, и еще немало вне стима. И пусть сейчас играть уже некогда, и зачастую интереснее писать код в свободное время, многие аспекты игры до сих пор восхищают. Например, реализация сервер меша на питон бекенде в 2003 году, с которой до сих пор, более 20 лет спустя, смогла сравниться лишь пара игр
Другой из этих аспектов - как разработчики год за годом обновляли графику игры, при этом сохраняя отличную оптимизацию и очень низкие системные требования. Игра регулярно выглядела лучше и лучше, при этом все еще оставаясь играбельной даже на встроенной видеокарте бюджетного ноутбука
Сейчас, к сожалению, авторы игры уже не очень охотно делятся техническими подробностями ее работы. Но так было не всегда, и можно найти старые подробные описания, как все было устроено под капотом
Вот один из примеров
https://www.eveonline.com/news/view/the-many-additions-of-v5plusplus
Статья от 2015 года, где рассказывают про новые оптимизации их доработанного PBR. Тут и увеличение допустимого количества материалов на одну модель, и новые способы упаковки текстур, и смена расчета модели отражений
Редизайны сайта не пощадили статью, и некоторые ссылки в ней теперь отображаются как обычный текст. Но при ручном открытии они все еще работают, и можно наглядно посмотреть, о чем же шла речь
В целом, я считаю, что у этих ребят можно многому научиться в техническом плане. Особенно в разрезе доработки визуала при сохранении отличной оптимизации
В конце концов, они смогли перейти от ужасно устаревшей графики к современной, не растеряв поддержки бюджетных устройств)
EVE Online
Textures, Shaders, and Dirt, Oh My! (The Many Additions of V5++) | EVE Online
.twentytwenty-container { width: 550px; min-height: 309px !important; margin-bottom: 2em; } Revamping every* ship in EVE is an intense process, leveraging the entirety of our art team from concept artists to tech artists and programmers to QA. It takes extreme…
👍3❤1
Только что понял, что еще не писал тут про WASM
Исправляюсь, так как штука очень полезная и в веб-разработке, и на десктопе, и в геймдеве
Есть такой забавный стандарт, WebAssembly
Изначально он задумывался исключительно как некий бинарный придаток к JavaScript, чтобы выносить в него дорогие вычисления на веб-страницах, и таким образом их ускорять
С тех пор много воды утекло - WASM сначала стал полноценный межбраузерным байткодом, потом получил доступ ко внешним API в рамках стандарта WASI, а затем стал использоваться для построения целых веб-приложений с околонулевым участием JavaScript
И потом мы прибыли к той же ключевой точке, которой когда-то достиг сам JS - мы вытащили WASM за пределы браузера и научились запускать его где угодно, вообще без единой строчки на JS и без его движка
WebAssembly на нативных платформах сохранил свои основные качества - переносимость, компактность, неплохая оптимизация, бинарный формат байткода, расширяемость, и, что главное, встроенная песочница для исполняемого кода
Стали появляться нативные WASM рантаймы, способные запускать его в довольно оптимизированном и легковесном виде хоть напрямую на нативных платформах, хоть встраиваясь в другие приложения в качестве библиотек
WASM стали развивать уже с учетом нативного исполнения, и с каждым годом добавлять все больше полезных фич, оптимизаций и возможностей. Как в сам язык, так и в WASI - многопоточность, поддержку сборщиков мусора, компонентную модель, и так далее
Сейчас он превратился в универсальный межъязыковой байткод, полностью портативный между разными платформами. То, чем не смогли стать ни байткод JVM, ни CLR у .NET. Почти любой существующий язык поддерживает сборку в WASM, делая его идеальным кандидатом для интероперабельности
Cloudflare пишут свои Workers на Rust, но не запускают его напрямую. Вместо этого, они собирают Rust код в WASM и запускают на серверах в WASM рантайме, чтобы обеспечить и динамическую загрузку кода, и поддержку многих других языков, кроме Rust - рантайму все равно, из какого языка был собран WASM байткод.
У нас на работе ситуация схожая - внутренний аналог AWS Lambda, бессерверных функций, реализован на WASM. Мы пишем код на Go (как на основном языке в компании), потом собираем его в байткод и запускаем на сервере, чтобы обеспечить динамическую подгрузку, обновление, и лимиты по потреблению ресурсов для этих функций
Zed, стремительно набирающий популярность редактор для разработки, реализовал свою систему плагинов на WASM - они предоставляют контракт API в специальном формате WIT, используемым компонентной моделью WASM. А дальше можно для любого языка сгенерировать из него привязки и реализовать плагин, который будет динамически загружен и исполнен в рантайме, встроенном в сам редактор
В отличие от динамических библиотек, нет никакой привязки к платформе, ABI компилятора и прочему. И есть встроенная песочница для исполняемого кода. Пользователь рантайма определяет, как ограничить ресурсы при запуске, и к каким API давать доступ, причем для каждого запускаемого WASM модуля отдельно
В отличие от скриптовых языков, это оптимизированный бинарный байткод, доступный для всех распространенных языков программирования
Исправляюсь, так как штука очень полезная и в веб-разработке, и на десктопе, и в геймдеве
Есть такой забавный стандарт, WebAssembly
Изначально он задумывался исключительно как некий бинарный придаток к JavaScript, чтобы выносить в него дорогие вычисления на веб-страницах, и таким образом их ускорять
С тех пор много воды утекло - WASM сначала стал полноценный межбраузерным байткодом, потом получил доступ ко внешним API в рамках стандарта WASI, а затем стал использоваться для построения целых веб-приложений с околонулевым участием JavaScript
И потом мы прибыли к той же ключевой точке, которой когда-то достиг сам JS - мы вытащили WASM за пределы браузера и научились запускать его где угодно, вообще без единой строчки на JS и без его движка
WebAssembly на нативных платформах сохранил свои основные качества - переносимость, компактность, неплохая оптимизация, бинарный формат байткода, расширяемость, и, что главное, встроенная песочница для исполняемого кода
Стали появляться нативные WASM рантаймы, способные запускать его в довольно оптимизированном и легковесном виде хоть напрямую на нативных платформах, хоть встраиваясь в другие приложения в качестве библиотек
WASM стали развивать уже с учетом нативного исполнения, и с каждым годом добавлять все больше полезных фич, оптимизаций и возможностей. Как в сам язык, так и в WASI - многопоточность, поддержку сборщиков мусора, компонентную модель, и так далее
Сейчас он превратился в универсальный межъязыковой байткод, полностью портативный между разными платформами. То, чем не смогли стать ни байткод JVM, ни CLR у .NET. Почти любой существующий язык поддерживает сборку в WASM, делая его идеальным кандидатом для интероперабельности
Cloudflare пишут свои Workers на Rust, но не запускают его напрямую. Вместо этого, они собирают Rust код в WASM и запускают на серверах в WASM рантайме, чтобы обеспечить и динамическую загрузку кода, и поддержку многих других языков, кроме Rust - рантайму все равно, из какого языка был собран WASM байткод.
У нас на работе ситуация схожая - внутренний аналог AWS Lambda, бессерверных функций, реализован на WASM. Мы пишем код на Go (как на основном языке в компании), потом собираем его в байткод и запускаем на сервере, чтобы обеспечить динамическую подгрузку, обновление, и лимиты по потреблению ресурсов для этих функций
Zed, стремительно набирающий популярность редактор для разработки, реализовал свою систему плагинов на WASM - они предоставляют контракт API в специальном формате WIT, используемым компонентной моделью WASM. А дальше можно для любого языка сгенерировать из него привязки и реализовать плагин, который будет динамически загружен и исполнен в рантайме, встроенном в сам редактор
В отличие от динамических библиотек, нет никакой привязки к платформе, ABI компилятора и прочему. И есть встроенная песочница для исполняемого кода. Пользователь рантайма определяет, как ограничить ресурсы при запуске, и к каким API давать доступ, причем для каждого запускаемого WASM модуля отдельно
В отличие от скриптовых языков, это оптимизированный бинарный байткод, доступный для всех распространенных языков программирования
1👍5❤1
Итак, как же всем этим счастьем воспользоваться?
Тут все сводится к выбору подходящего рантайма. Их довольно много, от интерпретатора wasm3 до написанного на Go Wazero. И каждый из них показывает разные результаты по поддержке стандарта, по производительности, и прочим параметрам
Но есть один рантайм, который рекомендуется самими авторами стандартов WASM и WASI, а также первым развивает и внедряет новые фичи (например, WasmGc, поддержку сборщиков мусора, они внедрили за год до появления в стандарте. И реализацию нового стандарта WASI Preview 2 обкатывают именно на нем)
Называется он Wasmtime, и именно его используем и мы на работе, и Zed для своих плагинов, и Cloudflare (но у последних он параллельно с V8)
Разработчикам на Rust тут повезло, потому что Wasmtime написан именно на этом языке, сверху донизу, и отлично интегрируется в проекты на нем. Однако он также имеет C и C++ API, что позволяет встроить его в код на других языках
Поскольку у нас на работе подавляющая часть кода на Go, мы изначально рассматривали Wazero, так как его элементарно интегрировать благодаря тому же языку. Однако Wasmtime оказался в 9.8 раз быстрее в исполнении байткода, поэтому решили, что такая разница в производительности стоит страданий с CGO и привязками
Wasmtime не только отличен сам по себе, он также основан на Cranelift от тех же разработчиков (ByteCodeAlliance), который оптимизируется под его нужды. А это аналог LLVM, который весит в сотню раз меньше, обрабатывает код в 10 раз быстрее, и генерируемые бинарные файлы, по разным данным, или работают с той же скоростью, или медленнее лишь на 5-10%. Это позволяет Cranelift использоваться и для обычной AOT компиляции, и для JIT в рантайме, включая WASM внутри Wasmtime
Cranelift, кроме упомянутого выше применения, также используется для экспериментального бекенда компилятора языка Rust, тогда как бекендом по-умолчанию является LLVM. На реальном проекте переход на Cranelift сократил время компиляции Rust кода на 59%
В общем, на данный момент WASM является отличным способом запуска кода, написанного на Rust, JS, TS, Python, Go, Zig, C, C++, Kotlin, C#, Java, PHP, Ruby, Swift, R, Objective-C, Dart, Odin, и многих других языках, на любой платформе и в контролируемом окружении. С возможностью как ограничить доступные ресурсы и API, так и предоставить собственные. И с динамическим запуском и выгрузкой, без критичных потерь производительности относительно натива благодаря JIT
При этом, в отличие от многих больших рантаймов языков вроде V8, упомянутый Wasmtime весит всего 10-15мб в виде библиотеки, в зависимости от платформы. А сборка с LTO способна еще больше сократить этот размер, исключив неиспользуемые участки кода
Тут все сводится к выбору подходящего рантайма. Их довольно много, от интерпретатора wasm3 до написанного на Go Wazero. И каждый из них показывает разные результаты по поддержке стандарта, по производительности, и прочим параметрам
Но есть один рантайм, который рекомендуется самими авторами стандартов WASM и WASI, а также первым развивает и внедряет новые фичи (например, WasmGc, поддержку сборщиков мусора, они внедрили за год до появления в стандарте. И реализацию нового стандарта WASI Preview 2 обкатывают именно на нем)
Называется он Wasmtime, и именно его используем и мы на работе, и Zed для своих плагинов, и Cloudflare (но у последних он параллельно с V8)
Разработчикам на Rust тут повезло, потому что Wasmtime написан именно на этом языке, сверху донизу, и отлично интегрируется в проекты на нем. Однако он также имеет C и C++ API, что позволяет встроить его в код на других языках
Поскольку у нас на работе подавляющая часть кода на Go, мы изначально рассматривали Wazero, так как его элементарно интегрировать благодаря тому же языку. Однако Wasmtime оказался в 9.8 раз быстрее в исполнении байткода, поэтому решили, что такая разница в производительности стоит страданий с CGO и привязками
Wasmtime не только отличен сам по себе, он также основан на Cranelift от тех же разработчиков (ByteCodeAlliance), который оптимизируется под его нужды. А это аналог LLVM, который весит в сотню раз меньше, обрабатывает код в 10 раз быстрее, и генерируемые бинарные файлы, по разным данным, или работают с той же скоростью, или медленнее лишь на 5-10%. Это позволяет Cranelift использоваться и для обычной AOT компиляции, и для JIT в рантайме, включая WASM внутри Wasmtime
Cranelift, кроме упомянутого выше применения, также используется для экспериментального бекенда компилятора языка Rust, тогда как бекендом по-умолчанию является LLVM. На реальном проекте переход на Cranelift сократил время компиляции Rust кода на 59%
В общем, на данный момент WASM является отличным способом запуска кода, написанного на Rust, JS, TS, Python, Go, Zig, C, C++, Kotlin, C#, Java, PHP, Ruby, Swift, R, Objective-C, Dart, Odin, и многих других языках, на любой платформе и в контролируемом окружении. С возможностью как ограничить доступные ресурсы и API, так и предоставить собственные. И с динамическим запуском и выгрузкой, без критичных потерь производительности относительно натива благодаря JIT
При этом, в отличие от многих больших рантаймов языков вроде V8, упомянутый Wasmtime весит всего 10-15мб в виде библиотеки, в зависимости от платформы. А сборка с LTO способна еще больше сократить этот размер, исключив неиспользуемые участки кода
1👍4