Я недавно изучал OpenGL. В играх нам нужно часто обновлять положение игровых объектов, и они будут постоянно появляться и исчезать с экрана. Таким образом, это означает, что при рендеринге нам также необходимо обновлять буфер вершин.
В контексте OpenGL, один интуитивный способ - использовать glBufferSubData для обновления тех, которые изменились.
Но я также прочитал в Интернете трюк под названием «Сирота», который создает новые данные буфера и загружает в них все данные вершин.
Кроме того, из-за изменений в состоянии и стоимости загрузки, несколько glBufferSubData могут также стоить дороже.
Вот мой вопрос,
- Какой метод лучше?
- Действительно ли киоски действительно важны в этом случае?
- Изменения состояния и стоимость загрузки действительно имеют значение в этом случае?
Спасибо!
Ответы:
Это сложный вопрос с множеством мелких деталей, которые действительно важны, производительность зависит от платформы и приложения. Поэтому вы должны определить возможные узкие места, прежде чем инвестировать в оптимизацию.
Тем не менее, во-первых, я предполагаю, что вы должны максимально уменьшить количество загрузок и обновлений, например, использовать экземпляры.
Во-вторых, обратите внимание, что графические процессоры не могут передавать буферы и визуализировать одновременно, поэтому все команды OpenGL в очереди команд обрабатываются устройством последовательно. Есть разные способы скопировать данные и / или сделать их доступными для использования графическим процессором.
Существуют различные способы потоковой передачи данных в графический процессор
1-
glBufferData
илиglBufferSubData
методИспользование
glBufferData
илиglBufferSubData
как memcpy. Вы передаете указатель, и операция DMA может быть выполнена, я сказал, может, потому что память может быть закреплена в памяти процессора и использоваться непосредственно GPU без фактической передачи памяти в GPU, в зависимости от флага использования (GL_STREAM). По мнению, вы должны попробовать это сначала, потому что это проще реализовать.2- получение указателя на внутреннюю память с использованием
glMapBuffer
Если вышеприведенное не достаточно хорошо, что вы можете использовать
glMapBuffer
, вы получаете указатель на внутреннюю память, и вы можете использовать этот указатель для непосредственного заполнения буфера, это хорошо для операций чтения и записи файла, так как вы можете напрямую отобразить данные файла в память GPU, а не копировать во временный буфер. Если вы не хотите отображать весь буфер, вы можете использоватьglMapBufferRange
его для отображения части буфера.Одна хитрость - создать большой буфер, использовать первую половину для рендеринга, а вторую - для обновления.
3- Буфер сирот
Что касается потерянного буфера, это можно сделать, используя glBufferData со значением null и теми же параметрами, что и у него. Драйвер вернет блок памяти, если он не используется. И будет использоваться при следующем вызове glBufferData (новая память не будет выделена).
Все упомянутые методы вызывают много дорогой синхронизации, опять же, графические процессоры не могут передавать буферы и визуализировать одновременно.
4-
Unsynchronized Buffers
Самый быстрый (и самый трудный для понимания) метод - использовать буферы без синхронизации, с которой вы можете использовать
GL_MAP_UNSYNCHRONIZED_BIT
флагglMapBufferRange
, проблема в том, что синхронизация не выполняется, поэтому мы можем загрузить данные в буфер, который будет использоваться, и, следовательно, все испортить. Вы можете использовать несколько буферов с несинхронизированным битом, чтобы сделать вещи немного проще.источник
Я делаю это по-другому. Может быть, там что-то не так. Некоторые скрытые опасные вещи.
(Я использую C # + OpenTK.GameWindow)
Для создания экземпляра объекта я использую отдельный объект Array Buffer Object для набора матриц модели для каждого экземпляра. В вершине поделились:
В моем коде C # матрицы хранятся в массиве с плавающей точкой
float[] mat4_array
Затем я связываю массив с объектом Array Buffer:
Каждый кадр модельных матриц обновляется. Чтобы обновить,
mat4_array
я просто звоню:и визуализировать сцену. используя
GL.DrawElementsInstanced
.Нет дополнительных вызовов OpenGL, и он работает отлично.
источник