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


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, и он работает отлично.


Какова цель иметь матрицу модели в качестве атрибута вершины?
Антон

Это для рисования объекта экземпляра. И это быстрее, чем массив, переданный как единая переменная.
Деннис
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.