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-движки часто имеют функцию под названием “батчинг”, которая объединяет многие вызовы в один вызов WebGL. Wonderland Engine реализует это на экстраординарном уровне, где сцены с десятками тысяч динамических объектов отрисовываются менее чем в десяти вызовах отрисовки автоматически.
Производительность WebGL на мобильных устройствах особенно страдает от вызовов отрисовки.
WebGL в Safari
В Safari (как на iOS, так и на MacOS) есть дополнительные вызовы WebGL, которые вызывают неожиданно большую нагрузку, например:
Особое внимание уделяйте использованию Uniform Buffers при оптимизации для Safari.
JavaScript
Язык, используемый для взаимодействия с API браузера, такими как WebGL, — это JavaScript. Основной причиной проблем с производительностью JavaScript является сборка мусора, которая автоматически находит и удаляет ненужную память.
Процесс сборки мусора может сделать производительность приложения WebGL непредсказуемой и ненадежной, так как вы не можете контролировать, когда она произойдет. В результате она часто происходит в моменты, когда может вызвать задержку в отрисовке, создавая иллюзию плохой производительности, даже если код в остальном хорошо оптимизирован.
Чтобы обеспечить стабильные частоты кадров, придерживайтесь строгого подхода к написанию кода JavaScript без создания мусора.
Safari на iOS и память
Приложения WebGL, которые хорошо работают в Safari, редки. Производительность WebGL в Safari добавляет некоторые дополнительные сложности. Мы написали целый пост в блоге о том, как оптимизировать для Safari.
Ограничения памяти
На iOS есть еще одна проблема: вкладки браузера обладают очень ограниченной памятью, особенно на старом оборудовании iPhone. Если вы превысите лимиты памяти, вкладка может перезагрузиться или зависнуть. Поскольку iPhone используют унифицированную память, RAM для CPU и GPU общая, и как текстурные данные, так и данные буфера, а также память JavaScript или WebAssembly будут учитывать в лимите.