Настройка OpenGL приложение ES

Производительность OpenGL приложения ES в iOS отличается от того из OpenGL в OS X или других настольных операционных системах. Несмотря на то, что мощные вычислительные устройства, основанные на iOS устройства не имеют эффективности ЗУ или мощности ЦП, которой обладают настольные или портативные компьютеры. Встроенные GPUs оптимизированы для более низкой памяти и использования питания, с помощью алгоритмов, отличающихся от тех типичный рабочий стол или ноутбук, который мог бы использовать GPU. Рендеринг Ваших графических данных неэффективно может привести к плохой частоте кадров или существенно сократить время работы от батареи основанного на iOS устройства.

Более поздние главы описывают много методов для улучшения производительности приложения; эта глава касается общих стратегий. Если иначе не маркировано, уведомление в этой главе принадлежит всем версиям OpenGL ES.

Отладьте и профилируйте свое приложение с XCode и инструментами

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

Наблюдайте за OpenGL ошибки ES в XCode и инструментах

 Ошибки ES OpenGL происходят, когда Ваше приложение использует OpenGL ES API неправильно (например, путем запроса операций, что используемое оборудование не способно к выполнению). Даже если Ваше содержание представляет правильно, эти ошибки могут указать проблемы производительности. Традиционный способ проверить на OpenGL ошибки ES состоит в том, чтобы вызвать glGetError функция; однако, неоднократно вызывание этой функции может значительно ухудшить производительность. Вместо этого используйте инструменты, описанные выше для тестирования на ошибки:

  • При профилировании приложения в Инструментах посмотрите подробную область для инструмента OpenGL ES Analyzer для просмотра любого OpenGL , о котором ошибки ES сообщили при записи.

  • При отладке приложения в XCode получите кадр для исследования команд рисования, используемых для создания его, а также любые ошибки, с которыми встречаются при выполнении тех команд.

Когда с OpenGL ошибка ES встречаются, можно также сконфигурировать XCode для остановки реализации программы. (См. Добавление OpenGL Точка прерывания при ошибке ES.)

Аннотируйте свой OpenGL код ES для информативной отладки и профилирования

Можно сделать отладку и профилирование более эффективными путем организации команд OpenGL ES в логические группы и добавления значимых меток к OpenGL объекты ES. Эти группы и метки появляются в OpenGL Отладчик Кадра ES в XCode как показано на рисунке 7-1, и в OpenGL ES Анализатор в Инструментах. Для добавления групп и меток используйте EXT_debug_marker и EXT_debug_label расширения.

Рисунок 7-1  Отладчик Кадра XCode прежде и после добавляющих групп маркера отладки

Когда у Вас есть последовательность команд рисования, представляющих единственную значимую работу — например, таща игровой символ — можно использовать маркер для группировки их для отладки. Перечисление 7-1 показывает, как сгруппировать текстуру, программу, массив вершины, и нарисовать требования единственного элемента сцены. Во-первых, это вызывает glPushGroupMarkerEXT функция для обеспечения понятного имени тогда это дает группу команд OpenGL ES. Наконец, это закрывает группу с вызовом к glPopGroupMarkerEXT функция.

Перечисление 7-1  Используя маркер отладки для аннотирования команд рисования

glPushGroupMarkerEXT(0, "Draw Spaceship");
glBindTexture(GL_TEXTURE_2D, _spaceshipTexture);
glUseProgram(_diffuseShading);
glBindVertexArrayOES(_spaceshipMesh);
glDrawElements(GL_TRIANGLE_STRIP, 256, GL_UNSIGNED_SHORT, 0);
glPopGroupMarkerEXT();

Можно использовать многократные вложенные маркеры для создания иерархии значимых групп в сложной сцене. Когда Вы используете GLKView класс для рисования OpenGL содержание ES это автоматически создает группу «Рендеринга», содержащую все команды в методе рисования. Любые маркеры, которые Вы создаете, вкладываются в этой группе.

Метки обеспечивают понятные имена для OpenGL объекты ES, такие как текстуры, программы программы построения теней и объекты массива вершины. Вызовите glLabelObjectEXT функция, чтобы дать объекту имя, которое будет показано при отладке и профилировании. Перечисление 7-2 иллюстрирует использование этой функции для маркировки объекта массива вершины. Если Вы используете GLKTextureLoader класс для загрузки данных текстуры это автоматически маркирует OpenGL , текстура ES возражает, что это создает с их именами файлов.

Перечисление 7-2  Используя метку отладки для аннотирования OpenGL объект ES

glGenVertexArraysOES(1, &_spaceshipMesh);
glBindVertexArrayOES(_spaceshipMesh);
glLabelObjectEXT(GL_VERTEX_ARRAY_OBJECT_EXT, _spaceshipMesh, 0, "Spaceship");

Общие рекомендации производительности

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

Сцены перерисовки только, когда изменяются данные сцены

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

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

Отключите неиспользованный OpenGL функции ES

Лучшее вычисление является тем, которое никогда не выполняет Ваше приложение. Например, если результат может быть предварительно вычислен и сохранен в Ваших данных модели, можно избежать выполнять то вычисление во время выполнения.

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

Если Ваше приложение использует OpenGL ES 1.1, отключите любые операции стандартной функции, которые не необходимы для рендеринга сцены. Например, если Ваше приложение не требует освещения или смешивания, отключите те функции. Точно так же, если Ваше приложение рисует только 2D модели, оно должно отключить тестирование глубины и вуаль.

Упростите свои модели распространения света

Эти инструкции применяются и к освещению стандартной функции в OpenGL ES 1.1 и к основанным на программе построения теней вычислениям освещения, которые Вы используете в своих пользовательских программах построения теней в OpenGL ES 2.0 или позже.

  • Используйте наименьшее количество возможных световых сигналов и самый простой тип освещения для Вашего приложения. Рассмотрите использование направленного света вместо местного освещения, требующего большего количества вычислений. Программы построения теней должны выполнить вычисления освещения в пространстве модели; рассмотрите использование более простых уравнений освещения в своих программах построения теней по более сложным алгоритмам освещения.

  • Предварительно вычислите свое освещение и сохраните значения цвета в текстуре, которая может быть выбрана обработкой фрагмента.

Используйте основанный на мозаике задержанный рендеринг эффективно

Все GPUs, используемые в устройствах на iOS, используют основанный на мозаике задержанный рендеринг (TBDR). При вызове OpenGL функции ES для представления команд рендеринга аппаратным средствам, команды буферизуются, пока большой список команд не накапливается. Аппаратные средства не начинают обрабатывать вершины и заштриховывать пиксели, пока Вы не представляете renderbuffer или сбрасываете буфер команд. Это тогда представляет эти команды как единственную работу путем деления кадрового буфера на мозаики и затем рисования команд один раз для каждой мозаики с каждой мозаикой, представляющей только примитивы, которые видимы в нем. ( GLKView класс представляет renderbuffer после Ваших возвратов метода рисования. Если Вы создаете свой собственный renderbuffer для дисплея с помощью CAEAGLLayer класс, Вы представляете его с помощью OpenGL контекст ES presentRenderbuffer: метод. glFlush или glFinish функционируйте сбрасывает буфер команд.)

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

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

Избегите логических буферных загрузок и хранилищ

Когда графический процессор TBDR начинает представлять мозаику, он должен сначала передать содержание той части кадрового буфера от общей памяти до памяти мозаики на GPU. Эта передача памяти, названная логической буферной загрузкой, занимает время и энергия. Чаще всего предыдущее содержание кадрового буфера не необходимо для рисования следующего кадра. Избегите стоимости производительности загрузки предыдущего содержимого буфера путем вызова glClear каждый раз, когда Вы начинаете представлять новый кадр.

Точно так же, когда GPU заканчивает представлять мозаику, она должна записать пиксельные данные мозаики обратно к общей памяти. Этой передаче, названной логической буферной памятью, также стоили производительности. По крайней мере одна такая передача необходима для каждого нарисованного кадра — цвет renderbuffer выведенный на экран на экране должен быть передан общей памяти, таким образом, это может быть представлено Базовой Анимацией. Другие присоединения кадрового буфера, используемые в Вашем алгоритме рендеринга (например, глубина, шаблон и мультивыборка буферов), не должны быть сохранены, потому что их содержание будет воссоздано на следующем нарисованном кадре. ES OpenGL автоматически хранит эти буферы к общей памяти — несению стоимости производительности — если Вы явно не лишаете законной силы их. Для лишения законной силы буфера используйте glInvalidateFramebuffer команда в OpenGL ES 3.0 или glDiscardFramebufferEXT команда в OpenGL ES 1.1 или 2.0. (Для получения дополнительной информации посмотрите Отбрасывание Ненужный Renderbuffers.), Когда Вы используете основной цикл получения, предоставленный GLKView класс, это автоматически лишает законной силы любую drawable глубину, шаблон или мультивыбирающие буферы, которые это создает.

Логическая буферная память и операции загрузки также происходят при переключении мест назначения рендеринга. Если Вы представляете к текстуре, то к кадровому буферу представления, то к той же текстуре снова, содержание текстуры должно неоднократно передаваться между общей памятью и GPU. Обработайте свои операции рисования в пакетном режиме так, чтобы все получение месту назначения рендеринга было сделано вместе. Когда Вы действительно переключаете кадровые буферы (использующий glBindFramebuffer или glFramebufferTexture2D функция или bindDrawable метод), лишите законной силы ненужные присоединения кадрового буфера, чтобы избежать вызывать логическую буферную память.

Используйте удаление невидимой поверхности эффективно

Графический процессор TBDR автоматически использует буфер глубины для выполнения удаления невидимой поверхности для всей сцены, гарантируя, что только одна программа построения теней фрагмента выполняется для каждого пикселя. Традиционные методы для сокращения обработки фрагмента не необходимы. Например, сортировка объектов или примитивов глубиной по всей длине эффективно копируют работу, выполненную GPU, трата процессорного времени.

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

Если Вы не можете избежать смешиваться, тестирование альфа-версии, или отбросить инструкции, рассмотреть следующие стратегии сокращения их влияния производительности:

  • Вид возражает непрозрачностью. Нарисуйте непрозрачные объекты сначала. Следующие графические объекты, требующие программы построения теней с помощью discard работа (или тестирование альфа-версии в OpenGL ES 1.1). Наконец, нарисуйте альфа-объединяемые объекты.

  • Смешивание требования объектов для обрезки или discard инструкции для сокращения количества обработанных фрагментов. Например, вместо того, чтобы рисовать квадрат для рендеринга 2D текстуры спрайта, содержащей главным образом вакуум, нарисуйте многоугольник, более близко приближающий форму изображения, как показано на рисунке 7-2. Стоимость производительности дополнительной обработки вершины намного меньше, чем та из рабочих программ построения теней фрагмента, результаты которых будут не использованы.

      Прозрачные объекты Обрезки рисунка 7-2 для сокращения обработки фрагмента
  • Используйте discard инструкция как можно раньше в Вашей программе построения теней фрагмента, чтобы избежать выполнять вычисления, результаты которых не использованы.

  • Вместо того, чтобы использовать тестирование альфа-версии или discard инструкции для уничтожения пикселей используйте альфу, смешивающуюся с обнуленной альфой. Цветной кадровый буфер не изменяется, но аппаратное обеспечение машинной графики может все еще использовать любую Z-буферную оптимизацию, которую это выполняет. Это действительно изменяет значение, сохраненное в буфере глубины, и так может потребовать наоборот сортировки прозрачных примитивов.

  • Если Ваша производительность ограничивается неизбежными операциями отбрасывания, рассмотрите стратегию рендеринга «Z-перед-передачей». Представьте свою сцену один раз с простой программой построения теней фрагмента, содержащей только Вашу логику отбрасывания (предотвращение дорогих вычислений освещения) для заполнения буфера глубины. Затем представьте свою сцену снова с помощью GL_EQUAL тест глубины функционирует и Ваши программы построения теней освещения. Хотя многопроходный рендеринг обычно подвергается потере производительности, этот подход может привести к лучшей производительности, чем однопроходный рендеринг, включающий большое количество операций отбрасывания.

Группа OpenGL команды ES для эффективного управления ресурсами

Пропускная способность памяти и вычислительные сбережения, описанные выше, выполняют лучше всего при обработке больших сцен. Но когда аппаратные средства получают команды OpenGL ES, требующие, чтобы он представил меньшие сцены, средство рендеринга теряет большую часть своей эффективности. Например, если Ваше приложение представляет пакеты треугольников с помощью текстуры и затем изменяет текстуру, OpenGL реализация ES должна или сразу спугнуть те команды или копировать текстуру — никакая опция не использует аппаратные средства эффективно. Точно так же любая попытка считать пиксельные данные из кадрового буфера требует, чтобы предыдущие команды были обработаны, если они изменили бы тот кадровый буфер.

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

Минимизируйте число команд рисования

Каждый раз, когда Ваше приложение представляет примитивы, которые будут обработаны OpenGL ES, CPU подготавливает команды к аппаратному обеспечению машинной графики. Если Ваше приложение использует многих glDrawArrays или glDrawElements вызовы для рендеринга сцены ее производительность может быть ограничена ресурсами CPU, полностью не используя GPU.

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

Используйте инстанцированное получение для минимизации, рисуют вызовы

Инстанцированные команды рисования позволяют Вам рисовать те же данные вершины многократно с помощью единственного вызова получения. Вместо того, чтобы использовать процессорное время, чтобы установить изменения между различными экземплярами сетки — такими как смещение позиции, матрица преобразования, цветные или координаты текстуры — и дать команду получения для каждого, инстанцированные рисующие перемещения обработка изменений экземпляра в код программы построения теней работает на GPU.

Данные вершины, снова использующиеся многократно, являются главным кандидатом на инстанцированное получение. Например, код в Перечислении 7-3 рисует объект в многократных позициях в сцене. Однако многие glUniform и glDrawArrays вызовы добавляют CPU наверху, сокращая производительность.

Перечисление 7-3  , Получающее много подобных объектов без инстанцирования

for (x = 0; x < 10; x++) {
    for (y = 0; y < 10; y++) {
        glUniform4fv(uniformPositionOffset, 1, positionOffsets[x][y]);
        glDrawArrays(GL_TRIANGLES, 0, numVertices);
    }
}

Принятие инстанцированного получения требует двух шагов: во-первых, замените циклы как вышеупомянутое с единственным вызовом к glDrawArraysInstanced или glDrawElementsInstanced. Эти вызовы иначе идентичны glDrawArrays или glDrawElements, но с дополнительным параметром, указывающим число экземпляров для рисования (100 для примера в Перечислении 7-3). Во-вторых, выберите и реализуйте одну из этих двух стратегий OpenGL , ES предусматривает использование информации на экземпляр в Вашем вершинном шейдере.

С экземпляром программы построения теней стратегия ID Ваш вершинный шейдер происходит или ищет информацию на экземпляр. Каждый раз выполнения вершинного шейдера, gl_InstanceID встроенная переменная содержит число, идентифицирующее экземпляр, в настоящее время нарисованный. Используйте это число, чтобы вычислить смещение позиции, цвет или другое изменение на экземпляр в коде программы построения теней, или искать информацию на экземпляр в универсальном массиве или другом запоминающем устройстве большого объема. Например, Перечисление 7-4 использует этот метод для рисования 100 экземпляров сетки, расположенной в 10 x 10 сеток.

   Использование вершинного шейдера OpenGL ES 3.0 перечисления 7-4 gl_InstanceID вычислить информацию на экземпляр

#version 300 es
 
in vec4 position;
 
uniform mat4 modelViewProjectionMatrix;
 
void main()
{
    float xOffset = float(gl_InstanceID % 10) * 0.5 - 2.5;
    float yOffset = float(gl_InstanceID / 10) * 0.5 - 2.5;
    vec4 offset = vec4(xOffset, yOffset, 0, 0);
 
    gl_Position = modelViewProjectionMatrix * (position + offset);
}

С инстанцированной стратегией массивов Вы храните информацию на экземпляр в атрибуте массива вершины. Ваш вершинный шейдер может тогда получить доступ к тому атрибуту для использования информации на экземпляр. Вызовите glVertexAttribDivisor функция, чтобы указать, как тот атрибут совершенствуется как OpenGL ES, рисует каждый экземпляр. Перечисление 7-5 демонстрирует установку массива вершины для инстанцированного получения, и Перечисление 7-6 показывает соответствующую программу построения теней.

Перечисление 7-5  Используя вершину приписывает для получения информации на экземпляр

#define kMyInstanceDataAttrib 5
 
glGenBuffers(1, &_instBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _instBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(instData), instData, GL_STATIC_DRAW);
glEnableVertexAttribArray(kMyInstanceDataAttrib);
glVertexAttribPointer(kMyInstanceDataAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0);
glVertexAttribDivisor(kMyInstanceDataAttrib, 1);

   Вершинный шейдер OpenGL ES 3.0 перечисления 7-6 с помощью инстанцировал массивы

#version 300 es
 
layout(location = 0) in vec4 position;
layout(location = 5) in vec2 inOffset;
 
uniform mat4 modelViewProjectionMatrix;
 
void main()
{
    vec4 offset = vec4(inOffset, 0.0, 0.0)
    gl_Position = modelViewProjectionMatrix * (position + offset);
}

Инстанцированное получение доступно в базовом OpenGL ES 3.0 API и в OpenGL ES 2.0 через EXT_draw_instanced и EXT_instanced_arrays расширения.

Минимизируйте OpenGL использование памяти ES

Ваше приложение для iOS совместно использует оперативную память с системой и другими приложениями для iOS. Память, выделенная для OpenGL ES, сокращает память, доступную для другого использования в Вашем приложении. С этим в памяти, выделите только память, что Вы нуждаетесь и освобождаете ее, как только для Вашего приложения больше не нужна она. Вот несколько способов, которыми можно сохранить память:

Система виртуальной памяти в iOS не использует файл подкачки. Когда условие низкой памяти обнаруживается, вместо того, чтобы писать энергозависимые страницы в диск, виртуальная память высвобождает энергонезависимую память, чтобы дать Вашему запущенному приложению память, в которой это нуждается. Ваше приложение должно стремиться использовать как можно меньше память и быть подготовленным избавиться от объектов, которые не важны для Вашего приложения. Ответ на условия низкой памяти покрыт подробно в Руководстве по программированию Приложения для iOS.

Знайте, что базовая анимация составляет производительность

Базовая Анимация составляет содержание renderbuffers с любыми другими уровнями в Вашей иерархии представления, независимо от того, были ли те уровни нарисованы с OpenGL ES, Кварц или другие графические библиотеки. Это полезно, потому что это означает, что OpenGL ES является первоклассным гражданином к Базовой Анимации. Однако смешивая OpenGL содержание ES с другим содержанием занимает время; когда используется неправильно, Ваше приложение может выполнить слишком медленно для достижения интерактивных частот кадров.

Для абсолютной лучшей производительности Ваше приложение должно положиться исключительно на OpenGL ES для рендеринга содержания. Измерьте представление, содержащее Ваш OpenGL содержание ES для соответствия экрана, удостоверьтесь opaque свойство установлено в YES (значение по умолчанию для GLKView объекты) и что никакие другие представления или Базовые Слои анимации не видимы.

Если Вы представляете в Базовом Слое анимации, составляющемся поверх других уровней, делая Ваш CAEAGLLayer непрозрачный объект сокращает — но не устраняет — стоимость производительности. Если Ваш CAEAGLLayer объект смешивается поверх уровней под ним в иерархии слоев, цветные данные renderbuffer должны быть в предварительно умноженном альфа-формате, который будет составлен правильно Базовой Анимацией. При смешивании OpenGL содержание ES поверх другого содержания имеет серьезную потерю производительности.