Когда VBOs быстрее, чем "простые" примитивы OpenGL (glBegin())?
После многих лет слуха о Vertex Buffer Objects (VBOs) я, наконец, решил экспериментировать с ними (мои вещи обычно не критичны по производительности, очевидно...)
Я опишу свой эксперимент ниже, но, чтобы сделать длинную историю коротким, я вижу непревзойденную производительность между "простым" прямым режимом (glBegin()/glEnd()), массивом вершин (сторона процессора) и VBO ( GPU). Я пытаюсь понять, почему это так, и при каких условиях я могу ожидать, что VBOs значительно затмевают своих примитивных (каламбур) предков.
Детали эксперимента
Для эксперимента я создал (статическое) трехмерное гауссовское облако большого числа точек. Каждая точка имеет связанную с ней информацию о вершине и цвете. Затем я вращал камеру вокруг облака в последовательных кадрах в виде "орбитального" поведения. Опять же, точки статичны, только глаз движется (через gluLookAt()). Данные генерируются один раз перед любым рендерингом и сохраняются в двух массивах для использования в цикле рендеринга.
Для прямого рендеринга весь набор данных отображается в одном блоке glBegin()/glEnd() с циклом, содержащим один вызов, каждый из которых - glColor3fv() и glVertex3fv().
Для вершинного массива и рендеринга VBO весь набор данных отображается с помощью одного вызова glDrawArrays().
Затем я просто запускаю его на минуту или около того в узкой петле и измеряю среднее значение FPS с помощью высокопроизводительного таймера.
Результаты работы ##
Как упоминалось выше, производительность была неотличима как на моем настольном компьютере (XP x64, 8GB RAM, 512 MB Quadro 1700), так и на моем ноутбуке (XP32, 4GB RAM, 256 MB Quadro NVS 110). Тем не менее, он имел масштаб, как и ожидалось, с количеством очков. Очевидно, я также отключил vsync.
Конкретные результаты запуска ноутбуков (рендеринг w/GL_POINTS):
glBegin()/glEnd():
- 1K pts → 603 FPS
- 10K pts → 401 FPS
- 100K pts → 97 FPS
- 1M pts → 14 FPS
Вершинные массивы (сторона процессора):
- 1K pts → 603 FPS
- 10K pts → 402 FPS
- 100K pts → 97 FPS
- 1M pts → 14 FPS
Объекты буфера вершин (сторона GPU):
- 1K pts → 604 FPS
- 10K pts → 399 FPS
- 100K pts → 95 FPS
- 1M pts → 14 FPS
Я отобрал те же данные с GL_TRIANGLE_STRIP и получил аналогичную неразличимость (хотя и медленнее, чем ожидалось, из-за дополнительной растеризации). Я тоже могу отправить эти цифры, если кто-то их захочет.
.
Вопрос (ы)
- Что дает?
- Что мне нужно сделать, чтобы реализовать обещанное повышение производительности VBOs?
- Что мне не хватает?
Ответы
Ответ 1
Существует множество факторов для оптимизации 3D-рендеринга.
обычно есть 4 узких места:
- CPU (создание вершин, вызовы APU, все остальное)
- Шина (передача CPU ↔ GPU)
- Вершина (вершинный шейдер над выполнением конвейера с фиксированной функцией)
- Пиксель (заполнение, выполнение шейдера фрагментов и ротации)
Ваш тест дает искаженные результаты, потому что у вас много CPU (и шины), при этом максимальная пропускная способность вершины или пикселя. VBOs используются для снижения количества процессоров (меньше вызовов api, параллельно с передачами DMA ЦП). Поскольку вы не связаны с ЦП, они не дают вам никакой выгоды. Это оптимизация 101. В игре, например, ЦП становится драгоценным, поскольку он необходим для других вещей, таких как ИИ и физика, а не только для выпуска тонны вызовов api. Легко видеть, что запись вершинных данных (например, 3 поплавков) непосредственно в указатель памяти намного быстрее, чем вызов функции, которая записывает 3 поплавки в память - по крайней мере, вы сохраняете циклы для вызова.
Ответ 2
Там может быть несколько недостатков:
-
Это дикая догадка, но карта вашего ноутбука может вообще не пропускать такую операцию (т.е. подражать ей).
-
Вы копируете данные в память графического процессора (через glBufferData
(GL_ARRAY_BUFFER
с параметром GL_STATIC_DRAW
или GL_DYNAMIC_DRAW
) или используете указатель на основной (не GPU) массив в памяти? ( который требует копирования каждого кадра, и поэтому производительность медленная)
-
Вы передаете индексы в качестве другого буфера, отправленного через glBufferData
и GL_ELEMENT_ARRAY_BUFFER
params?
Если эти три вещи выполнены, прирост производительности большой.
Для Python (v/pyOpenGl) он примерно в 1000 раз быстрее на массивах размером более пары 100 единиц,
С++ до 5 раз быстрее, но на массивах 50k-10m вершин.
Вот мои результаты теста для С++ (Core2Duo/8600GTS):
pts vbo glb/e ratio
100 3900 3900 1.00
1k 3800 3200 1.18
10k 3600 2700 1.33
100k 1500 400 3.75
1m 213 49 4.34
10m 24 5 4.80
Таким образом, даже с вершинами 10 м это была нормальная частота кадров, а с glB/e она была вялой.
Ответ 3
В качестве дополнительной заметки:
"Прямой режим" (glBegin/glEnd) не поддерживается:
OpenGLES.
OpenGL 3.x.
Итак, если вы когда-либо планируете переносить свое приложение на мобильную платформу (например, iPhone)
даже не привыкнуть к нему.
Я преподаю OpenGL в университете, и слайд, объясняющий glBegin/glEnd, имеет большую красную рамку вокруг
с добавочным жирным заголовком "НЕ ИСПОЛЬЗУЙТЕ".
Использование вершинных массивов - всего две строки, и вы сохраняете циклы с самого начала.
Ответ 4
Из чтения Красной книги я помню прохождение, в котором говорилось, что VBOs, возможно, быстрее в зависимости от аппаратного обеспечения. Некоторые аппаратные средства оптимизируют их, а другие - нет. Возможно, ваше оборудование не работает.
Ответ 5
14Mpoints/s не очень много. Он подозревает. можем ли мы увидеть полный код, выполняющий рисунок, а также инициализацию? (сравните 14M/s с 240M/s (!), который получает Слава Вишняков). Еще более подозрительно, что он снижается до 640 К/с для ничьих 1К (по сравнению с его 3,8 Мбит/с, который, по-видимому, закрыт ~ 3800 SwapBuffers).
Я бы оценил тест, не измеряя, что, по вашему мнению, он измеряет.
Ответ 6
Предполагая, что я правильно это помню, мой учитель OpenGL, который хорошо известен в сообществе OpenGL, сказал, что они быстрее статической геометрии, и это будет много времени на типичной игре, это будет стул для столов и небольшой статический сущности.