WebGL Производительность
WebGL имеет плохую репутацию: многие разработчики считают, что WebGL медленный и не может отрисовывать сложную 3D-графику. Однако множество примеров демонстрируют, что это не так, но опыт разработчиков снова и снова подтверждает это мнение.
Оборудование, на котором выполняется код WebGL, не замедляется для браузеров. Так почему разработчики считают, что невозможно быстро отрисовывать 3D-графику в вебе?
Эта статья проведет вас через лучшие практики повышения производительности WebGL и покажет, как вы тоже можете достигнуть быстрой 3D-отрисовки в вебе.
CPU и GPU
В 3D-приложении часть кода работает на CPU, а другая часть на ускоренном оборудовании — GPU.
WebGL спроектирован так, чтобы использовать GPU для ускорения графических приложений в вебе. WebGL можно понимать как функции, которые отправляют задачи на графическое оборудование и получают результат.
Код, который создает задания для GPU, работает на CPU, и для WebGL мы управляем им через JavaScript.
Нагрузка от браузера
Поскольку одна из целей браузера — защита пользователя от потенциально вредоносных сайтов, он будет проверять безопасность каждого вызова WebGL, который выполняет сайт через JavaScript. Также необходимо изолировать процесс выполнения кода сайта, и любые вызовы от и к этому процессу должны быть преобразованы в формат, пригодный для пересылки, который называется “маршалингом”.
Оба этих процесса ― маршалинг и проверка вызовов WebGL ― требуют дополнительных вычислительных ресурсов, что увеличивает накладные расходы по сравнению с аналогичными вызовами в нативной среде.
Избегайте вызовов WebGL
Чтобы достичь высокой производительности WebGL, стоит избегать вызовов с высокой нагрузкой. Вы легко сможете определить, какие вызовы особенно затратны, профилируя ваше WebGL-приложение.
Некоторые из неожиданно затратных вызовов:
Отличный способ избежать этих вызовов — следить за состоянием WebGL и уменьшать проверку ошибок в продукционных сборках вашего WebGL-приложения.
Избегайте вызовов отрисовки
Главный секрет отличной производительности WebGL — это минимизация количества вызовов отрисовки. Вызовом отрисовки является любая функция, использующая одну из следующих функций WebGL:
- drawArrays()
- drawElements()
- drawArraysInstanced()
- drawElementsInstanced()
- drawBuffers()
- drawRangeElements()
Есть много способов минимизировать количество вызовов. Например, можно использовать один вызов функции для отрисовки множества объектов, вместо многократного вызова функции для тех же объектов. Можно использовать инстансирование для рендеринга большого количества одинаковых моделей или WEBGL_multi_draw для рендеринга разных объектов с одним шейдером.
3D-движки часто обладают функцией “батчинг”, которая совмещает несколько вызовов в один. Wonderland Engine доводит это до экстремального уровня, где сцены с десятками тысяч динамических объектов отрисовываются в менее чем десять вызовов отрисовки автоматически.
Производительность WebGL на мобильных устройствах особенно чувствительна к количеству вызовов отрисовки.
Избегайте вызовов отрисовки (мобильные устройства)
Мы упоминаем этот аспект дважды, чтобы подчеркнуть важность: на мобильных устройствах вызовы отрисовки критически важны даже без дополнительных накладных расходов браузера. Даже при нативной разработке с Unity или напрямую с GLES, вам нужно держать количество вызовов отрисовки на минимальном уровне.
Если ваша цель — достичь отличной производительности WebGL на мобильных устройствах, вам нужно уделять особое внимание вызовам отрисовки.
WebGL на Safari
В Safari (на iOS и MacOS) есть дополнительные вызовы WebGL, которые вызывают неожиданно большие накладные расходы, например:
При оптимизации для Safari уделяйте особое внимание использованию Uniform Buffers.
JavaScript
Язык, используемый для взаимодействия с API браузера, такими как WebGL, — это JavaScript. Одной из основных проблем с производительностью JavaScript является сборка мусора, которая автоматически находит и очищает ненужную память.
Этот процесс может сделать производительность WebGL приложений непредсказуемой и ненадежной, так как вы не можете контролировать его время. В результате, он часто происходит в моменты, которые могут вызвать задержку в отрисовке, создавая иллюзию плохой производительности, даже если в остальном код оптимизирован.
Чтобы обеспечить стабильные частоты кадров, придерживайтесь строгой политики написания JavaScript без создания мусора.
iOS Safari и память
Приложения WebGL, хорошо работающие в Safari, встречаются редко. Производительность WebGL в Safari добавляет ряд сложностей. Мы посвятили целый пост в блоге оптимизации для Safari.
Ограничения памяти
На iOS столкинемся с еще одной проблемой: вкладки браузера имеют очень ограниченную память, особенно на более старом оборудовании iPhone. Если вы превысите лимиты памяти, вкладка может перезагрузиться или зависнуть. Поскольку iPhone используют унифицированную память, RAM для CPU и GPU общая, и как данные текстур и буферов, так и память JavaScript или WebAssembly будут учитываться в ограничении.