Лучшие практики WebGL
=====================

Эта статья содержит советы и рекомендации по улучшению качества ваших WebGL приложений. Следование данным советам поможет улучшить совместимость ваших веб-приложений с большим количеством устройств и браузеров, а также увеличить их производительность.

Чего следует избегать
---------------------

* Убедитесь, что ваше приложение не выдаёт какие-либо ошибки WebGL, возвращаемые функцией `getError(). В Firefox при каждой ошибке (до определённого предела) или при любой другой проблеме в работе WebGL выводится JavaScript предупреждение с подробным описанием. Вам же не хочется, что бы ваше приложение выдавало множество ошибок в консоль, не так ли?`
* Не следует использовать `#ifdef GL_ES в шейдерах WebGL. Несмотря на то что в некоторых ранних примерах используются эти директивы, это не обязательно в том случае, если проверяемое условие всегда истинно.`
* Использование высокой точности (`highp` precision) во фрагментных шейдерах может приводить к несовместимости вашего приложения с некоторыми устаревшими мобильными устройствами. Вы можете использовать среднюю точность (`mediump), но помните, что это может привести к некорректному результату отрисовки из-за потери данных на большинстве мобильных устройств, причём этот некорректный результат не будет заметен на обычном компьютере. В общем, только использование высокой точности (highp) в вершинном и фрагментном шейдерах является более надёжными решением, если нет возможности тщательно проверить работу шейдеров на различных платформах. В Firefox версии 11 и выше реализована функция WebGL getShaderPrecisionFormat(), которая позволяет проверить, поддерживается ли высокая точность и, более того, запросить реальную точность всех поддерживаемых квалификаторов точности.`
О чем следует помнить
---------------------

* Некоторые возможности WebGL зависят от клиента. Перед тем как задействовать ту или иную возможность, используйте функцию WebGL `getParameter() чтобы определить, какие возможности поддерживаются на клиенте. Например, максимально допустимый размер двухмерной текстуры можно узнать с помощью вызова webgl.getParameter(webgl.MAX_TEXTURE_SIZE). В Firefox версии 10 и выше реализован параметр webgl.min_capability_mode, позволяющий имитировать минимальные значения возможностей WebGL для проверки переносимости приложения.`
* В частности, использование текстур в вершинном шейдере возможно только если значение `webgl.getParameter(webgl.MAX_VERTEX_TEXTURE_IMAGE_UNITS) больше ноля. Как правило, эта возможность не поддерживается на текущих мобильных устройствах.`
* Доступность большинства расширений WebGL зависит от клиента. Если это возможно, проектируйте приложение так, чтобы оно оставалось работоспособным даже в случае, когда используемое расширение недоступно. В Firefox версии 10 и выше есть настройка `webgl.disable-extensions, позволяющая сымитировать отсутствие всех расширений для проверки переносимости приложения.`
* Рендеринг в floating-point текстуру может не выполняться даже если расширение `OES_texture_float поддерживается. Обычно это случается на современных мобильных устройствах. Проверить эту возможность можно с помощью функции WebGL checkFramebufferStatus().`
* Вы можете выполнять отрисовку на холсте, реальные размеры которого отличается от значений, определённых в таблице стилей. При проблемах с производительностью рассмотрите возможность рендеринга в более низком разрешении. (*Уменьшение области рендеринга ускорит обработку пиксельных шейдеров, например, эффектов постобработки*, *однако, на скорость работы вершинных шейдеров это не повлияет.* *прим. перев.).*
Общие советы по повышению производительности
--------------------------------------------

* Все, что требует синхронизации ЦП и ГП потенциально приводит в уменьшению производительности. Поэтому избегайте в цикле отрисовки следующих вызовов функций WebGL: `getError()`, `readPixels()` и `finish()`. Вызовы функций, получающих значения, такие как `getParameter()` и `getUniformLocation() тоже должны рассматриваться как медленные и их значения следует сохраняться в переменных JavaScript.`
* Несколько больших операций отрисовки выполняются быстрее, чем много мелких. Если вам нужно нарисовать 1000 спрайтов, попробуйте реализовать это одним вызовом функции `drawArrays()` или `drawElements()`. Вы также можете использовать вырожденные (плоские) треугольники для рисования нескольких объектов за один вызов `drawArrays().`
* Уменьшение переключений состояний также увеличивает производительность. В частности, если есть возможность упаковать несколько изображений в одну текстуру *(т.н. текстурный атлас, прим. перев.)* и отображать требуемое изображение с помощью поправок текстурных координат, то это приведёт к уменьшению переключений между текстурами, что увеличит производительность.
    + В некоторых редких случаях разные одноцветные изображения можно упаковать в разные цветовые каналы текстуры.
* Маленькие текстуры обрабатываются быстрее, чем большие. Используйте mipmapping для ускорения отрисовки.
* Простые шейдеры выполняются быстрее, чем сложные. В частности, условия (if) замедляют работу. Операции деления и математические функции, например, `log() должны также рассматриваться как дорогие.`
    + Однако сегодня даже мобильные устройства обладают мощными графическими процессорами которые способны быстро обрабатывать относительно сложные шейдерные программы. Более того, шейдеры компилируются в машинные коды, которые могут быть оптимизированы под конкретный процессор. Может оказаться, что дорогой вызов функции может быть скомпилирован в несколько (или даже в одну) процессорную инструкцию. Частично это справедливо для функций GLSL, выполняющих операции над векторами, таких как `normalize()`, `dot()` и `mix()`. Лучшим советом будет использовать встроенные функции, нежели пытаться реализовать, например, собственную версию скалярного произведения или линейной интерполяции, которые будут скомпилированы в набор сложных и неоптимальных инструкций процессора.
* Выносите как можно больше операций в вершинный шейдер. Из-за того, что в процессе отрисовки фрагментные шейдеры выполняются гораздо чаще, чем вершинные, любые вычисления, которые можно выполнить с вершинами и интерполировать между пикселями, будут работать быстрее (интерполяция будет "бесплатна", т.к. это этап конвейера WebGL). Например, простая анимация текстурированной поверхности может быть реализована с помощью преобразований текстурных координат (простейший вариант - прибавлять значение uniform-вектора к attribute-вектору текстурных координат). Если результат будет визуально приемлем, то такой вариант будет работать быстрее, чем реализация во фрагментном шейдере.
* Всегда задействуйте атрибут вершин c нулевым индексом. Отрисовка с неактивным вершинным атрибутом с индексом 0 вынуждает браузер выполнять сложную эмуляцию настольного OpenGL (например, как на Mac OSX). Вызывайте функцию `bindAttribLocation()` чтобы вершинный атрибут использовал нулевой индекс и активируйте сам атрибут с помощью функции `enableVertexAttribArray()`.