Для пропаривания вершинного буфера, множественные glBufferSubData VS Orphaning?

8

Я недавно изучал OpenGL. В играх нам нужно часто обновлять положение игровых объектов, и они будут постоянно появляться и исчезать с экрана. Таким образом, это означает, что при рендеринге нам также необходимо обновлять буфер вершин.

В контексте OpenGL, один интуитивный способ - использовать glBufferSubData для обновления тех, которые изменились.

Но я также прочитал в Интернете трюк под названием «Сирота», который создает новые данные буфера и загружает в них все данные вершин.

Кроме того, из-за изменений в состоянии и стоимости загрузки, несколько glBufferSubData могут также стоить дороже.

Вот мой вопрос,

  1. Какой метод лучше?
  2. Действительно ли киоски действительно важны в этом случае?
  3. Изменения состояния и стоимость загрузки действительно имеют значение в этом случае?

Спасибо!

Ифэн
источник
Почему вы хотите повторно загрузить геометрию, когда вы можете только обновить матрицу? другими словами, вам не нужно повторно загружать вершины, когда нужно изменить только преобразования.
concept3d
@ concept3d Да, ты прав! Я сделал ошибку при написании описания проблемы. Спасибо, что напомнили мне об этом! Я уже обновил описания.
YiFeng

Ответы:

9

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

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

Во-вторых, обратите внимание, что графические процессоры не могут передавать буферы и визуализировать одновременно, поэтому все команды 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, проблема в том, что синхронизация не выполняется, поэтому мы можем загрузить данные в буфер, который будет использоваться, и, следовательно, все испортить. Вы можете использовать несколько буферов с несинхронизированным битом, чтобы сделать вещи немного проще.

concept3d
источник
0

Я делаю это по-другому. Может быть, там что-то не так. Некоторые скрытые опасные вещи.

(Я использую C # + OpenTK.GameWindow)

Для создания экземпляра объекта я использую отдельный объект Array Buffer Object для набора матриц модели для каждого экземпляра. В вершине поделились:

#version 400
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 texcoord;
layout (location = 4) in mat4 model_matrix;

uniform mat4 proj_matrix, view_matrix;

В моем коде C # матрицы хранятся в массиве с плавающей точкой float[] mat4_array

Затем я связываю массив с объектом Array Buffer:

public void bind_data_instances()
{
    GL.BindBuffer(BufferTarget.ArrayBuffer, id_InstanceBO);
    GL.BufferData(BufferTarget.ArrayBuffer, mat4_array.Length * sizeof(float), mat4_array, BufferUsageHint.DynamicDraw);
}

Каждый кадр модельных матриц обновляется. Чтобы обновить, mat4_arrayя просто звоню:

Buffer.BlockCopy(mat4_array_new, 0, mat4_array, 0, sizeof(float) * mat4_models.Length);

и визуализировать сцену. используя GL.DrawElementsInstanced.

Нет дополнительных вызовов OpenGL, и он работает отлично.

Деннис
источник
Какова цель иметь матрицу модели в качестве атрибута вершины?
Антон
Это для рисования объекта экземпляра. И это быстрее, чем массив, переданный как единая переменная.
Деннис