Методы наиболее успешной практики для работы с данными вершины
Для рендеринга кадра с помощью OpenGL ES приложение конфигурирует графический конвейер и представляет графические примитивы, которые будут нарисованы. В некоторых приложениях все примитивы нарисованы с помощью той же конвейерной конфигурации; другие приложения могут представить различные элементы кадра с помощью различных методов. Но независимо от того который примитивы Вы используете в своем приложении или как конвейер сконфигурирован, Ваше приложение обеспечивает вершины для OpenGL ES. Эта глава обеспечивает напоминание на данных вершины и следует за ними с предназначенным уведомлением для того, как эффективно обработать данные вершины.
Вершина состоит из одного или более атрибутов, таких как позиция, цвет, нормальные, или координаты текстуры. OpenGL ES 2.0 или 3,0 приложения свободны определить свои собственные атрибуты; каждый атрибут в данных вершины соответствует переменной атрибута, действующей как ввод к вершинному шейдеру. Приложение OpenGL 1.1 использует атрибуты, определенные конвейером стандартных функций.
Вы определяете атрибут как вектор, состоящий из одного - четырех компонентов. Все компоненты в атрибуте совместно используют тип общих данных. Например, цвет мог бы быть определен как четыре GLubyte
компоненты (красный, зеленый, синий, альфа). Когда атрибут загружается в переменную программы построения теней, любые компоненты, которые не предоставлены в данных приложения, заполнены в значениями по умолчанию OpenGL ES. Последний компонент заполнен 1
, и другие неуказанные компоненты заполнены 0
, как проиллюстрировано на рисунке 8-1.
Ваше приложение может сконфигурировать атрибут, чтобы быть константой, что означает, что те же значения используются для всех вершин, представленных как часть команды получения или массив, что означает что каждая вершина значение для того атрибута. Когда Ваше приложение вызывает функцию в OpenGL ES для рисования ряда вершин, данные вершины копируются с приложения на аппаратное обеспечение машинной графики. Аппаратное обеспечение машинной графики, чем действия на данных вершины, обрабатывая каждую вершину в программе построения теней, собирая примитивы и растеризируя их в кадровый буфер. Одно преимущество OpenGL , который ES - то, что это стандартизирует на единственном наборе функций для представления данных вершины OpenGL ES, демонтируя более старые и менее эффективные механизмы, которые были предоставлены OpenGL.
Приложения, которые должны представить большое количество примитивов для рендеринга кадра, должны тщательно управлять своими данными вершины и как они обеспечивают его для OpenGL ES. Методы, описанные в этой главе, могут быть получены в итоге в нескольких основных принципах:
Сократите размер своих данных вершины.
Сократите предварительную обработку, которая должна произойти перед OpenGL ES может передать данные вершины аппаратному обеспечению машинной графики.
Сократите время, проведенное, копируя данные вершины в аппаратное обеспечение машинной графики.
Сократите вычисления, выполняемые для каждой вершины.
Упростите свои модели
Аппаратное обеспечение машинной графики основанных на iOS устройств очень мощно, но изображения, которые оно выводит на экран, являются часто очень маленькими. Вам не нужны чрезвычайно сложные модели для представления востребованной графики на iOS. Сокращение количества вершин раньше рисовало модель, непосредственно сокращает размер данных вершины и вычислений, выполняемых на Ваших данных вершины.
Можно сократить сложность модели при помощи некоторых следующих методов:
Обеспечьте многократные версии своей модели на разных уровнях подробности и выберите надлежащую модель во время выполнения на основе расстояния объекта от камеры и размерностей дисплея.
Используйте текстуры для избавления от необходимости некоторую информацию о вершине. Например, карта удара может использоваться для добавления подробности к модели, не добавляя больше данных вершины.
Некоторые модели добавляют вершины для улучшения подробных данных освещения или рендеринга качества. Когда значения вычислены для каждой вершины и интерполированы через треугольник во время этапа растеризации, это обычно делается. Например, при направлении центра внимания на центр треугольника его эффект мог бы остаться незамеченным, потому что самая яркая часть центра внимания не направлена на вершину. Путем добавления вершин Вы обеспечиваете дополнительные точки interpolant, за счет увеличения размера Ваших данных вершины и вычислений, выполняемых на модели. Вместо того, чтобы добавить дополнительные вершины, рассмотрите движущиеся вычисления в этап фрагмента конвейера вместо этого:
Если Ваше приложение использует OpenGL ES 2.0 или позже, то Ваше приложение выполняет вычисление в вершинном шейдере и присваивает его переменной переменной. Переменное значение интерполировано аппаратным обеспечением машинной графики и передано программе построения теней фрагмента как ввод. Вместо этого присвойте вводы вычисления переменным переменным и выполните вычисление в программе построения теней фрагмента. Выполнение этого изменяет стоимость выполнения того вычисления от стоимости на вершину до стоимости на фрагмент, сокращает давление на этап вершины и больше давления на этап фрагмента конвейера. Сделайте это, когда Ваше приложение блокируется на обработке вершины, вычисление недорого, и количество вершины может быть значительно сокращено изменением.
Если Ваше приложение использует OpenGL ES 1.1, можно выполнить освещение на фрагмент с помощью освещения DOT3. Вы делаете это путем добавления, что удар отображает текстуру для содержания нормальной информации и применяя карту удара с помощью работы объединения текстуры с
GL_DOT3_RGB
режим.
Избегите хранить константы в массивах атрибута
Если Ваши модели включают атрибуты, который использует данные, остающиеся постоянными через всю модель, не копируйте те данные для каждой вершины. OpenGL ES 2.0 и 3,0 приложения могут или установить постоянный атрибут вершины или использовать универсальное значение программы построения теней для содержания значения вместо этого. Приложение OpenGL ES 1.1 должно использовать функцию атрибута на вершину такой как glColor4ub
или glTexCoord2f
вместо этого.
Используйте самые маленькие приемлемые типы для атрибутов
При указании размера каждого из компонентов атрибута выберите самый маленький тип данных, обеспечивающий приемлемые результаты. Вот некоторые инструкции:
Укажите цвета вершины с помощью четырех компонентов байта без знака (
GL_UNSIGNED_BYTE
).Укажите координаты текстуры с помощью 2 или 4 байтов без знака (
GL_UNSIGNED_BYTE
) или короткое целое без знака (GL_UNSIGNED_SHORT
). Не упаковывайте многократные наборы координат текстуры в единственный атрибут.Избегайте использования OpenGL ES
GL_FIXED
тип данных. Это требует того же объема памяти какGL_FLOAT
, но обеспечивает меньший диапазон значений. Все устройства на iOS поддерживают аппаратные средства модули с плавающей точкой, таким образом, значения с плавающей точкой могут быть обработаны более быстро.Контексты OpenGL ES 3.0 поддерживают более широкий диапазон небольших типов данных, такой как
GL_HALF_FLOAT
иGL_INT_2_10_10_10_REV
. Они часто обеспечивают достаточную точность для атрибутов, таких как normals с меньшим местом, чемGL_FLOAT
.
Если Вы указываете меньшие компоненты, уверены, что Вы переупорядочиваете свой формат вершины, чтобы избежать неправильно выравнивать Ваши данные вершины. Посмотрите Избегают Неправильно выровненных Данных Вершины.
Используйте чередованные данные вершины
Можно указать данные вершины как серию массивов (также известный как структура массивов) или как массив, где каждый элемент включает многократные атрибуты (массив структур). Предпочтительный формат на iOS является массивом структур с единственным чередованным форматом вершины. Чередованные данные обеспечивают лучшую местность памяти для каждой вершины.
Исключение к этому правилу - когда Ваше приложение должно обновить некоторые данные вершины на уровне, отличающемся от остальной части данных вершины, или если некоторые данные могут быть совместно использованы двумя или больше моделями. В любом случае можно хотеть разделить данные атрибута на две или больше структуры.
Избегите неправильно выровненных данных вершины
Когда Вы разрабатываете свою структуру вершины, выравниваете начало каждого атрибута к смещению, которое является или кратным числом его размера компонента или 4 байтами, какой бы ни больше. Когда атрибут неправильно выравнивается, iOS должен выполнить дополнительную обработку прежде, чем передать данные аппаратному обеспечению машинной графики.
На рисунке 8-4 позиция и нормальные данные каждый определяются как три коротких целых для в общей сложности шести байтов. Нормальные данные начинаются при смещении 6
, который является кратным числом собственного размера (2 байта), но не является кратным числом 4 байтов. Если бы эти данные вершины были представлены iOS, то iOS должен был бы занять время, чтобы скопировать и выровнять данные прежде, чем передать их аппаратным средствам. Для фиксации этого явно добавьте два байта дополнения после каждого атрибута.
Используйте треугольные полосы для пакетной обработки данных вершины
Используя треугольные полосы значительно сокращает количество вычислений вершины, которые OpenGL ES должен выполнить на Ваших моделях. На левой стороне рисунка 8-5 три треугольника указаны с помощью в общей сложности девяти вершин. C, E и G фактически указывают ту же вершину! Путем указания данных как треугольную полосу можно сократить количество вершин от девять до пять.
Иногда, Ваше приложение может объединить больше чем одну треугольную полосу в единственную большую треугольную полосу. Все полосы должны совместно использовать те же требования рендеринга. Это означает:
Необходимо использовать ту же программу построения теней для рисования всех треугольных полос.
Необходимо быть в состоянии представить все треугольные полосы, не изменяя состояния OpenGL.
Треугольные полосы должны совместно использовать те же атрибуты вершины.
Для слияния двух треугольных полос копируйте последнюю вершину первой полосы и первую вершину второй полосы, как показано на рисунке 8-6. Когда эта полоса представлена OpenGL ES, треугольники ДИ, EEF, EFF, и FFG считают вырожденными и не обработанные или растеризированные.
Для лучшей производительности Ваши модели должны быть представлены как единственная индексируемая треугольная полоса. Чтобы избежать указывать данные для той же вершины многократно в буфере вершины, используйте отдельный индексный буфер и нарисуйте треугольную полосу с помощью glDrawElements
функция (или glDrawElementsInstanced
или glDrawRangeElements
функции, если надлежащий).
В OpenGL ES 3.0 можно использовать примитивную функцию перезапуска для слияния треугольных полос, не используя вырожденные треугольники. Когда эта опция активирована, OpenGL , ES обрабатывает самое большое значение в индексном буфере как команда, чтобы закончить одну треугольную полосу и запустить другого. Перечисление 8-1 демонстрирует этот подход.
Перечисление 8-1 Используя примитивный перезапуск в OpenGL ES 3.0
// Prepare index buffer data (not shown: vertex buffer data, loading vertex and index buffers) |
GLushort indexData[11] = { |
0, 1, 2, 3, 4, // triangle strip ABCDE |
0xFFFF, // primitive restart index (largest possible GLushort value) |
5, 6, 7, 8, 9, // triangle strip FGHIJ |
}; |
// Draw triangle strips |
glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX); |
glDrawElements(GL_TRIANGLE_STRIP, 11, GL_UNSIGNED_SHORT, 0); |
Где возможно, вершина вида и индексные данные, таким образом, треугольники, совместно использующие общие вершины, нарисованы обоснованно друг близко к другу в треугольной полосе. Аппаратное обеспечение машинной графики часто кэши недавние вычисления вершины, чтобы избежать повторно вычислять вершину.
Используйте буферные объекты вершины управлять копированием данных вершины
Перечисление 8-2 обеспечивает функцию, которую простое приложение могло бы использовать для обеспечения позиции и цветных данных к вершинному шейдеру. Это включает два атрибута и конфигурирует каждого для указания на чередованную структуру вершины. Наконец, это вызывает glDrawElements
функционируйте для рендеринга модели как единственной треугольной полосы.
Перечисление 8-2 , Представляющее данные вершины программе программы построения теней
typedef struct _vertexStruct |
{ |
GLfloat position[2]; |
GLubyte color[4]; |
} vertexStruct; |
void DrawModel() |
{ |
const vertexStruct vertices[] = {...}; |
const GLubyte indices[] = {...}; |
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, |
sizeof(vertexStruct), &vertices[0].position); |
glEnableVertexAttribArray(GLKVertexAttribPosition); |
glVertexAttribPointer(GLKVertexAttribColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, |
sizeof(vertexStruct), &vertices[0].color); |
glEnableVertexAttribArray(GLKVertexAttribColor); |
glDrawElements(GL_TRIANGLE_STRIP, sizeof(indices)/sizeof(GLubyte), GL_UNSIGNED_BYTE, indices); |
} |
Этот код работает, но неэффективно. Каждый раз DrawModel
вызывается, индекс и данные вершины копируются в OpenGL ES и передаются аппаратному обеспечению машинной графики. Если данные вершины не изменяются между вызовами, эти ненужные копии могут повлиять на производительность. Для предотвращения ненужных копий приложение должно хранить свои данные вершины в буферном объекте вершины (VBO). Поскольку OpenGL , ES принадлежит вершина, буферизует память объекта, это может сохранить буфер в памяти, которая более доступна для аппаратного обеспечения машинной графики, или предварительно обработайте данные в предпочтительный формат для аппаратного обеспечения машинной графики.
Перечисление 8-3 создает пару буферных объектов вершины, один для содержания данных вершины и второго для индексов полосы. В каждом случае код генерирует новый объект, обязывает его быть текущим буфером и заполняет буфер. CreateVertexBuffers
когда приложение инициализируется, был бы вызван.
Перечисление 8-3 , Создающее вершину, буферизует объект
GLuint vertexBuffer; |
GLuint indexBuffer; |
void CreateVertexBuffers() |
{ |
glGenBuffers(1, &vertexBuffer); |
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); |
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); |
glGenBuffers(1, &indexBuffer); |
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); |
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); |
} |
Перечисление 8-4 изменяет Перечисление 8-2 для использования буферных объектов вершины. Основное отличие в Перечислении 8-4 то, что параметры к glVertexAttribPointer
функции больше не указывают на массивы вершины. Вместо этого каждый - смещение в буферный объект вершины.
Получение перечисления 8-4 с вершиной буферизует объект
void DrawModelUsingVertexBuffers() |
{ |
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); |
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, |
sizeof(vertexStruct), (void *)offsetof(vertexStruct, position)); |
glEnableVertexAttribArray(GLKVertexAttribPosition); |
glVertexAttribPointer(GLKVertexAttribColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, |
sizeof(vertexStruct), (void *)offsetof(vertexStruct, color)); |
glEnableVertexAttribArray(GLKVertexAttribColor); |
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); |
glDrawElements(GL_TRIANGLE_STRIP, sizeof(indices)/sizeof(GLubyte), GL_UNSIGNED_BYTE, (void*)0); |
} |
Буферные подсказки использования
Предыдущий пример инициализировал буфер вершины один раз и никогда не изменял его содержание впоследствии. Можно изменить содержание буфера вершины. Ключевая роль проекта буферных объектов вершины - то, что приложение может сообщить OpenGL ES, как это использует данные, хранившие в буфере. Реализация ES OpenGL может использовать эту подсказку для изменения стратегии, которую это использует для того, чтобы хранить данные вершины. В Перечислении 8-3, каждом вызове к glBufferData
функция обеспечивает подсказку использования как последний параметр. Передача GL_STATIC_DRAW
в glBufferData
говорит OpenGL ES, что содержание обоих буферов, как никогда ожидают, не изменится, который дает OpenGL ES больше возможностей оптимизировать, как и где хранятся данные.
OpenGL спецификация ES определяет следующие случаи использования:
GL_STATIC_DRAW
для буферов вершины, представляющихся много раз, и чье содержание указано один раз и никогда не изменяется.GL_DYNAMIC_DRAW
для буферов вершины, представляющихся много раз, и чье содержание изменяется во время цикла рендеринга.GL_STREAM_DRAW
для буферов вершины, представляющихся небольшим числом раз и затем отбрасывающихся.
В iOS, GL_DYNAMIC_DRAW
и GL_STREAM_DRAW
эквивалентны. Можно использовать glBufferSubData
функционируйте для обновления содержимого буфера, но выполнение так подвергается потере производительности, потому что оно сбрасывает буфер команд и ожидает всех команд для завершения. Двойная или тройная буферизация может сократить эту стоимость производительности несколько. (См. Двойную буферизацию Использования для Предотвращения Конфликтов Ресурса.) Для лучшей производительности, используйте glMapBufferRange
функция в OpenGL ES 3.0 или соответствующая функция, предоставленная
расширение в OpenGL ES 2.0 или 1.1.EXT_map_buffer_range
Если различные атрибуты в Вашем формате вершины требуют различных образцов использования, разделяют данные вершины на многократные структуры и выделяют отдельный буферный объект вершины для каждого набора атрибутов то общее использование доли характеристики. Перечисление 8-5 изменяет предыдущий пример для использования отдельного буфера для содержания цветных данных. Путем выделения цветного буфера с помощью GL_DYNAMIC_DRAW
подсказка, OpenGL , ES может выделить тот буфер так, чтобы Ваше приложение поддержало разумную производительность.
Перечисление 8-5 , Получающее модель с многократной вершиной, буферизует объекты
typedef struct _vertexStatic |
{ |
GLfloat position[2]; |
} vertexStatic; |
typedef struct _vertexDynamic |
{ |
GLubyte color[4]; |
} vertexDynamic; |
// Separate buffers for static and dynamic data. |
GLuint staticBuffer; |
GLuint dynamicBuffer; |
GLuint indexBuffer; |
const vertexStatic staticVertexData[] = {...}; |
vertexDynamic dynamicVertexData[] = {...}; |
const GLubyte indices[] = {...}; |
void CreateBuffers() |
{ |
// Static position data |
glGenBuffers(1, &staticBuffer); |
glBindBuffer(GL_ARRAY_BUFFER, staticBuffer); |
glBufferData(GL_ARRAY_BUFFER, sizeof(staticVertexData), staticVertexData, GL_STATIC_DRAW); |
// Dynamic color data |
// While not shown here, the expectation is that the data in this buffer changes between frames. |
glGenBuffers(1, &dynamicBuffer); |
glBindBuffer(GL_ARRAY_BUFFER, dynamicBuffer); |
glBufferData(GL_ARRAY_BUFFER, sizeof(dynamicVertexData), dynamicVertexData, GL_DYNAMIC_DRAW); |
// Static index data |
glGenBuffers(1, &indexBuffer); |
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); |
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); |
} |
void DrawModelUsingMultipleVertexBuffers() |
{ |
glBindBuffer(GL_ARRAY_BUFFER, staticBuffer); |
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, |
sizeof(vertexStruct), (void *)offsetof(vertexStruct, position)); |
glEnableVertexAttribArray(GLKVertexAttribPosition); |
glBindBuffer(GL_ARRAY_BUFFER, dynamicBuffer); |
glVertexAttribPointer(GLKVertexAttribColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, |
sizeof(vertexStruct), (void *)offsetof(vertexStruct, color)); |
glEnableVertexAttribArray(GLKVertexAttribColor); |
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); |
glDrawElements(GL_TRIANGLE_STRIP, sizeof(indices)/sizeof(GLubyte), GL_UNSIGNED_BYTE, (void*)0); |
} |
Консолидируйте изменения состояния массива вершины Используя объекты массива вершины
Бросьте более внимательный взгляд на DrawModelUsingMultipleVertexBuffers
функция в Перечислении 8-5. Это включает много атрибутов, связывает многократные буферные объекты вершины и конфигурирует атрибуты для указания в буферы. Весь тот код инициализации чрезвычайно статичен; ни один из параметров не изменяется от кадра до кадра. Если эта функция вызвана каждый раз, когда приложение представляет кадр, существует большое ненужное служебное реконфигурирование графического конвейера. Если приложение рисует много различных видов моделей, реконфигурировать конвейер может стать узким местом. Вместо этого используйте объект массива вершины сохранить полную конфигурацию атрибута. Объекты массива вершины являются частью базовой спецификации OpenGL ES 3.0 и доступны в OpenGL ES 2.0 и 1.1 через
расширение.OES_vertex_array_object
Рисунок 8-7 показывает конфигурацию в качестве примера с двумя объектами массива вершины. Каждая конфигурация независима от другого; каждый объект массива вершины может сослаться на различный набор атрибутов вершины, которые могут быть сохранены в том же буферном объекте вершины или разделении через несколько буферных объектов вершины.
Перечисление 8-6 обеспечивает, код раньше конфигурировал первый объект массива вершины, показанный выше. Это генерирует идентификатор для нового объекта массива вершины и затем связывает объект массива вершины с контекстом. После этого это выполняет те же вызовы для конфигурирования атрибутов вершины, как это было бы, если код не использовал объекты массива вершины. Конфигурация сохранена к связанному объекту массива вершины вместо к контексту.
Перечисление 8-6 , Конфигурирующее вершину, выстраивает объект
void ConfigureVertexArrayObject() |
{ |
// Create and bind the vertex array object. |
glGenVertexArrays(1,&vao1); |
glBindVertexArray(vao1); |
// Configure the attributes in the VAO. |
glBindBuffer(GL_ARRAY_BUFFER, vbo1); |
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, |
sizeof(staticFmt), (void*)offsetof(staticFmt,position)); |
glEnableVertexAttribArray(GLKVertexAttribPosition); |
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_UNSIGNED_SHORT, GL_TRUE, |
sizeof(staticFmt), (void*)offsetof(staticFmt,texcoord)); |
glEnableVertexAttribArray(GLKVertexAttribTexCoord0); |
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, |
sizeof(staticFmt), (void*)offsetof(staticFmt,normal)); |
glEnableVertexAttribArray(GLKVertexAttribNormal); |
glBindBuffer(GL_ARRAY_BUFFER, vbo2); |
glVertexAttribPointer(GLKVertexAttribColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, |
sizeof(dynamicFmt), (void*)offsetof(dynamicFmt,color)); |
glEnableVertexAttribArray(GLKVertexAttribColor); |
// Bind back to the default state. |
glBindBuffer(GL_ARRAY_BUFFER,0); |
glBindVertexArray(0); } |
Для рисования код связывает объект массива вершины и затем представляет команды рисования как прежде.
Для лучшей производительности Ваше приложение должно сконфигурировать каждый объект массива вершины один раз, и никогда не изменять его во время выполнения. Если необходимо изменить объект массива вершины в каждом кадре, создайте многократные объекты массива вершины вместо этого. Например, приложение, использующее двойную буферизацию, могло бы сконфигурировать один набор объектов массива вершины для кадров с нечетным номером и второй набор для четных кадров. Каждый набор объектов массива вершины указал бы на буферные объекты вершины, используемые для рендеринга того кадра. Когда конфигурация объекта массива вершины не изменяется, OpenGL , ES может кэшировать информацию о вершине, форматирует и улучшается, как это обрабатывает те атрибуты вершины.
Буферы карты в клиентскую память для быстрых обновлений
Одна из более сложных проблем в OpenGL , проект приложения ES работает с динамическими ресурсами, особенно если Ваши данные вершины должны изменить каждый кадр. Эффективно балансирующийся параллелизм между CPU и GPU требует тщательно управляющей передачи данных между пространством памяти Вашего приложения и OpenGL память ES. Традиционные методы, такие как использование glBufferSubData
функционируйте, может сократить производительность, потому что они вынуждают GPU ожидать, в то время как данные передаются, даже если это могло бы иначе представлять от данных в другом месте в том же буфере.
Например, можно хотеть и изменить вершину, буферизуют и рисуют ее содержание на каждом, проходят через цикл рендеринга высокой частоты кадров. Команда получения от последнего представленного кадра может все еще использовать GPU, в то время как CPU пытается получить доступ к буферной памяти для подготовки к рисованию следующего кадра — то, чтобы заставлять буферный вызов обновления блокировать дальнейшую работу CPU, пока не сделан GPU. Можно улучшить производительность в таких сценариях путем ручной синхронизации CPU и доступа GPU к буферу.
glMapBufferRange
функция обеспечивает более эффективный способ динамично обновить буферы вершины. (Эта функция доступна как базовый API в OpenGL ES 3.0 и через
расширение в OpenGL ES 1.1 и 2.0.) Используют эту функцию для получения указателя на область OpenGL память ES, которую можно тогда использовать для записи новых данных. EXT_map_buffer_range
glMapBufferRange
функция позволяет отображаться любого поддиапазона хранения данных буфера в клиентскую память. Это также поддерживает подсказки, допускающие асинхронную буферную модификацию, когда Вы используете функцию вместе с объектом синхронизации OpenGL, как показано в Перечислении 8-7.
Перечисление 8-7 , Динамично обновляющее вершину, буферизует с ручной синхронизацией
GLsync fence; |
GLboolean UpdateAndDraw(GLuint vbo, GLuint offset, GLuint length, void *data) { |
GLboolean success; |
// Bind and map buffer. |
glBindBuffer(GL_ARRAY_BUFFER, vbo); |
void *old_data = glMapBufferRange(GL_ARRAY_BUFFER, offset, length, |
GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT | |
GL_MAP_UNSYNCHRONIZED_BIT ); |
// Wait for fence (set below) before modifying buffer. |
glClientWaitSync(fence, GL_SYNC_FLUSH_COMMANDS_BIT, |
GL_TIMEOUT_IGNORED); |
// Modify buffer, flush, and unmap. |
memcpy(old_data, data, length); |
glFlushMappedBufferRange(GL_ARRAY_BUFFER, offset, length); |
success = glUnmapBuffer(GL_ARRAY_BUFFER); |
// Issue other OpenGL ES commands that use other ranges of the VBO's data. |
// Issue draw commands that use this range of the VBO's data. |
DrawMyVBO(vbo); |
// Create a fence that the next frame will wait for. |
fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); |
return success; |
} |
UpdateAndDraw
функция в этом примере использует glFenceSync
функция для установления точки синхронизации или предела, сразу после представления команд рисования, использующих определенный буферный объект. Это тогда использует функцию glClientWaitSync (на следующем, проходят через цикл рендеринга) проверять что точка синхронизации прежде, чем изменить буферный объект. Если те команды рисования заканчивают выполняться на GPU, прежде чем цикл рендеринга возвратится вокруг, выполнение CPU не блокирует и UpdateAndDraw
функция продолжает изменять буфер и рисовать следующий кадр. Если GPU не закончил выполнять те команды, glClientWaitSync
функциональные блоки дальнейшее выполнение CPU до GPU достигают предела. Путем ручного размещения точек синхронизации только вокруг разделов кода с потенциальными конфликтами ресурса, можно минимизировать, сколько времени CPU ожидает GPU.