Содержание

WebGL2Fundamentals.org

Fix, Fork, Contribute

WebGL2 Cross Platform Issues

Вероятно, не будет шоком то, что не все WebGL программы работают на всех устройствах или браузерах.

Вот список большинства проблем, с которыми вы можете столкнуться, с верхушки моей головы

Производительность

Топовый GPU, вероятно, работает в 100 раз быстрее, чем низкоуровневый GPU. Единственный способ обойти это, который я знаю, это либо целиться низко, либо давать пользователю опции, как делают большинство Desktop PC приложений, где они могут выбрать производительность или качество.

Память

Аналогично топовый GPU может иметь от 12 до 24 гигабайт оперативной памяти, тогда как низкоуровневый GPU вероятно имеет меньше 1 гигабайта. (Я старый, поэтому для меня удивительно, что низкоуровневый = 1 гигабайт, поскольку я начал программировать на машинах с 16k до 64k памяти 😜)

Ограничения устройства

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

Примеры включают:

  • Максимальный размер текстуры

    2048 или 4096 кажутся разумными ограничениями. По крайней мере, по состоянию на 2020 год выглядит, что 99% устройств поддерживают 4096, но только 50% поддерживают > 4096.

    Примечание: максимальный размер текстуры - это максимальное измерение, которое GPU может обработать. Это не означает, что у GPU достаточно памяти для этого измерения в квадрате (для 2D текстуры) или в кубе (для 3D текстуры). Например, некоторые GPU имеют максимальный размер 16384. Но 3D текстура 16384 с каждой стороны потребовала бы 16 терабайт памяти!!!

  • Максимальное количество вершинных атрибутов в одной программе

    В WebGL1 минимально поддерживается 8. В WebGL2 это 16. Если вы используете больше, чем это, то ваш код не сработает на машине только с минимумом

  • Максимальное количество uniform векторов

    Они указаны отдельно для вершинных шейдеров и фрагментных шейдеров.

    В WebGL1 это 128 для вершинных шейдеров и 16 для фрагментных шейдеров В WebGL2 это 256 для вершинных шейдеров и 224 для фрагментных шейдеров

    Обратите внимание, что uniforms могут быть “упакованы”, поэтому число выше - это сколько vec4 можно использовать. Теоретически вы могли бы иметь в 4 раза больше float uniforms. но есть алгоритм, который их помещает. Вы можете представить пространство как массив с 4 столбцами, одна строка для каждого из максимальных uniform векторов выше.

    +-+-+-+-+
    | | | | |   <- один vec4
    | | | | |   |
    | | | | |   |
    | | | | |   V
    | | | | |   максимальные uniform векторы строк
    | | | | |
    | | | | |  
    | | | | |
    ...
    
    

    Сначала выделяются vec4 с mat4 как 4 vec4. Затем vec3 помещаются в оставшееся пространство. Затем vec2 с последующими float. Так что представьте, что у нас было 1 mat4, 2 vec3, 2 vec2 и 3 float

    +-+-+-+-+
    |m|m|m|m|   <- mat4 занимает 4 строки
    |m|m|m|m|
    |m|m|m|m|
    |m|m|m|m|
    |3|3|3| |   <- 2 vec3 занимают 2 строки
    |3|3|3| |
    |2|2|2|2|   <- 2 vec2 могут втиснуться в 1 строку 
    |f|f|f| |   <- 3 float помещаются в одну строку
    ...
    
    

    Далее, массив uniforms всегда вертикальный, поэтому, например, если максимально разрешенные uniform векторы - 16, то вы не можете иметь 17-элементный float массив и на самом деле, если у вас был один vec4, это заняло бы целую строку, поэтому осталось только 15 строк, что означает, что самый большой массив, который вы можете иметь, будет 15 элементов.

    Мой совет, однако, не рассчитывайте на идеальную упаковку. Хотя спецификация говорит, что алгоритм выше требуется для прохождения, слишком много комбинаций для тестирования, что все драйверы проходят. Просто будьте в курсе, если вы приближаетесь к лимиту.

    примечание: varyings и attributes не могут быть упакованы.

  • Максимальные varying векторы.

    WebGL1 минимум 8. WebGL2 это 16.

    Если вы используете больше, ваш код не будет работать на машине только с минимумом.

  • Максимальные texture units

    Здесь 3 значения.

    1. Сколько texture units существует
    2. Сколько texture units может ссылаться вершинный шейдер
    3. Сколько texture units может ссылаться фрагментный шейдер
    WebGL1WebGL2
    мин texture units которые существуют832
    мин texture units на которые может ссылаться вершинный шейдер0!16
    мин texture units на которые может ссылаться фрагментный шейдер816

    Важно отметить 0 для вершинного шейдера в WebGL1. Обратите внимание, что это, вероятно, не конец света. По-видимому, ~97% всех устройств поддерживают по крайней мере 4. Тем не менее, вы можете захотеть проверить, чтобы вы могли либо сказать пользователю, что ваше приложение не будет работать для них, либо вы можете откатиться к каким-то другим шейдерам.

Есть и другие ограничения. Чтобы их посмотреть, вы вызываете gl.getParameter с следующими значениями.

MAX_TEXTURE_SIZE макс размер текстуры
MAX_VERTEX_ATTRIBS количество атрибутов которые вы можете иметь
MAX_VERTEX_UNIFORM_VECTORS количество vec4 uniforms которые может иметь вершинный шейдер
MAX_VARYING_VECTORS количество varyings которые у вас есть
MAX_COMBINED_TEXTURE_IMAGE_UNITSколичество texture units которые существуют
MAX_VERTEX_TEXTURE_IMAGE_UNITS количество texture units на которые может ссылаться вершинный шейдер
MAX_TEXTURE_IMAGE_UNITS количество texture units на которые может ссылаться фрагментный шейдер
MAX_FRAGMENT_UNIFORM_VECTORS количество vec4 uniforms которые может иметь фрагментный шейдер
MAX_CUBE_MAP_TEXTURE_SIZE макс размер cubemap
MAX_RENDERBUFFER_SIZE макс размер renderbuffer
MAX_VIEWPORT_DIMS макс размер viewport

Это не весь список. Например, максимальный размер точки и максимальная толщина линии, но вы должны в основном предполагать, что максимальная толщина линии 1.0 и что POINTS полезны только для простых демо, где вам все равно на проблемы с обрезкой.

WebGL2 добавляет еще несколько. Несколько общих:

MAX_3D_TEXTURE_SIZE макс размер 3D текстуры
MAX_DRAW_BUFFERS количество color attachments которые вы можете иметь
MAX_ARRAY_TEXTURE_LAYERS макс слоев в 2D texture array
MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS количество varyings которые вы можете выводить в отдельные буферы при использовании transform feedback
MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTSколичество varyings которые вы можете выводить при отправке их всех в один буфер
MAX_COMBINED_UNIFORM_BLOCKS количество uniform blocks которые вы можете использовать в целом
MAX_VERTEX_UNIFORM_BLOCKS количество uniform blocks которые может использовать вершинный шейдер
MAX_FRAGMENT_UNIFORM_BLOCKS количество uniform blocks которые может использовать фрагментный шейдер

Разрешение буфера глубины

Несколько действительно старых мобильных устройств используют 16-битные буферы глубины. В противном случае, AFAICT 99% устройств используют 24-битный буфер глубины, поэтому вам, вероятно, не нужно беспокоиться об этом.

readPixels format/type комбинации

Только определенные format/type комбинации гарантированно работают. Другие комбинации опциональны. Это покрыто в этой статье.

framebuffer attachment комбинации

Framebuffers могут иметь 1 или более attachments текстур и renderbuffers.

В WebGL1 только 3 комбинации attachments гарантированно работают.

  1. один format = RGBA, type = UNSIGNED_BYTE texture как COLOR_ATTACHMENT0
  2. format = RGBA, type = UNSIGNED_BYTE texture как COLOR_ATTACHMENT0 и format = DEPTH_COMPONENT renderbuffer прикрепленный как DEPTH_ATTACHMENT
  3. format = RGBA, type = UNSIGNED_BYTE texture как COLOR_ATTACHMENT0 и format = DEPTH_STENCIL renderbuffer прикрепленный как DEPTH_STENCIL_ATTACHMENT

Все другие комбинации зависят от реализации, которую вы проверяете, вызывая gl.checkFramebufferStatus и видя, вернул ли он FRAMEBUFFER_COMPLETE.

WebGL2 гарантирует возможность записи во многие другие форматы, но все еще имеет ограничение в том, что любая комбинация может потерпеть неудачу! Ваша лучшая ставка может быть, если все color attachments одного формата, если вы прикрепляете больше 1.

Расширения

Многие функции WebGL1 и WebGL2 опциональны. Вся суть наличия API под названием getExtension в том, что он может потерпеть неудачу, если расширение не существует, и поэтому вы должны проверять эту неудачу и не слепо предполагать, что это сработает.

Вероятно, наиболее часто отсутствующее расширение в WebGL1 и WebGL2 - это OES_texture_float_linear, что является способностью фильтровать floating point текстуру, что означает способность поддерживать установку TEXTURE_MIN_FILTER и TEXTURE_MAX_FILTER на что-либо, кроме NEAREST. Многие мобильные устройства не поддерживают это.

В WebGL1 другое часто отсутствующее расширение - это WEBGL_draw_buffers, что является способностью прикреплять больше 1 color attachment к framebuffer, все еще около 70% для десктопа и почти ни одного для смартфонов (это кажется неправильным). В основном любое устройство, которое может запускать WebGL2, должно также поддерживать WEBGL_draw_buffers в WebGL1, но все же, это, по-видимому, все еще проблема. Если вы нуждаетесь рендерить в несколько текстур одновременно, вероятно, вашей странице нужен высокоуровневый GPU в целом. Тем не менее, вы должны проверить, поддерживает ли устройство пользователя это, и если нет, предоставить дружелюбное объяснение.

Для WebGL1 следующие 3 расширения кажутся почти универсально поддерживаемыми, поэтому, хотя вы можете захотеть предупредить пользователя, что ваша страница не будет работать, если они отсутствуют, вероятно, что у этого пользователя крайне старое устройство, которое все равно не собиралось хорошо запускать вашу страницу.

Это ANGLE_instance_arrays (способность использовать instanced drawing), OES_vertex_array_object (способность хранить все состояние атрибута в объекте, чтобы вы могли поменять все это состояние одним вызовом функции. Смотрите это), и OES_element_index_uint (способность использовать UNSIGNED_INT 32 битные индексы с drawElements).

локации атрибутов

Полу-общая ошибка - не искать локации атрибутов. Например, у вас есть вершинный шейдер вроде

attribute vec4 position;
attribute vec2 texcoord;

uniform mat4 matrix;

varying vec2 v_texcoord;

void main() {
   gl_Position = matrix * position;
   v_texcoord = texcoord;
}

Ваш код предполагает, что position будет атрибутом 0 и texcoord будет атрибутом 1, но это не гарантировано. Поэтому он работает для вас, но не срабатывает для кого-то другого. Часто это может быть ошибкой в том, что вы не делали это намеренно, но через ошибку в коде вещи работают, когда локации одним способом, но не другим.

Есть 3 решения.

  1. Всегда искать локации.

  2. Назначать локации, вызывая gl.bindAttribLocation перед вызовом gl.linkProgram

  3. Только WebGL2, устанавливать локации в шейдере как в

    #version 300 es
    layout(location = 0) vec4 position;
    latout(location = 1) vec2 texcoord;
    ...
    

    Решение 2 кажется наиболее D.R.Y., тогда как решение 3 кажется наиболее W.E.T., если только вы не генерируете ваши текстуры во время выполнения.

GLSL неопределенное поведение

Несколько GLSL функций имеют неопределенное поведение. Например, pow(x, y) является неопределенным, если x < 0. Есть более длинный список в низу статьи о spot lighting.

Проблемы точности шейдеров

В 2020 году самая большая проблема здесь в том, что если вы используете mediump или lowp в ваших шейдерах, то на десктопе GPU действительно будет использовать highp, но на мобильных они действительно будут mediump и или lowp, и поэтому вы не заметите никаких проблем при разработке на десктопе.

Смотрите эту статью для более подробной информации.

Поведение Points, Lines, Viewport, Scissor

POINTS и LINES в WebGL могут иметь максимальный размер 1, и на самом деле для LINES это теперь наиболее общее ограничение. Далее, обрезаются ли точки, когда их центр находится вне viewport, определяется реализацией. Смотрите низ этой статьи.

Аналогично, обрезает ли viewport только вершины или также пиксели, является неопределенным. Scissor всегда обрезает пиксели, поэтому включайте scissor test и устанавливайте размер scissor, если вы устанавливаете viewport меньше, чем то, что вы рисуете, и вы рисуете LINES или POINTS.

Есть предложения или замечания? Создайте issue на GitHub.
comments powered by Disqus