QT QOpenGLWidget: как изменить значения отдельных вершин в VBO без использования копии блока данных? - PullRequest
0 голосов
/ 15 октября 2018

Я не знаю, возможно ли это:

  • У меня есть массив вершин QVector3D, которые я копирую в VBO
  • , иногда я хочу изменить толькоz значение диапазона вершин между значениями (x1, y1) и (x2, y2) - соответствующие вершины строго следуют друг за другом
  • моя «хорошая» идея - изменять только значения z с прямымдоступ к VBO.

Я много искал, но все решения, которые я видел, используют memcpy, что-то вроде этого:

m_vboPos.bind();
GLfloat* PosBuffer = (GLfloat*) (m_vboPos.map(QOpenGLBuffer::WriteOnly));
if (PosBuffer != (GLfloat*) NULL) {
    memcpy(PosBuffer, m_Vertices.constData(), m_Vertices.size() * sizeof(QVector3D));
m_vboPos.unmap();
m_vboPos.release();

Но это для копирования блоки данных.

Я не думаю, что использование memcpy для изменения только 1 значения с плавающей запятой в каждой рассматриваемой вершине будет очень эффективным (у меня есть несколько миллионов вершин в VBO).

Я просто хотел бы оптимизировать, потому что копирование миллионов вершин занимает (слишком) много времени: есть ли способ достичь моей цели (без memcpy?), Только для одного поплавка здесь и там?(уже пробовал, но не смог, мне что-то не хватает)

Ответы [ 2 ]

0 голосов
/ 16 октября 2018

Метод glMap отлично работает и действительно БЫСТРО!

Большое спасибо genpfault, увеличение скорости настолько велико, что 3D-рендеринг больше не прерывается.

Вот мой новыйКод, упрощенный, чтобы предложить легкий для понимания ответ:

vertexbuffer.bind();
GLfloat* posBuffer = (GLfloat*) (vertexbuffer.map(QOpenGLBuffer::WriteOnly));

if (posBuffer != (GLfloat*) NULL) {
    int index = NumberOfVertices(area.y + 1, image.cols); // index of first vertex on line area.y
    for (row = ...) for (col = ...) {
        if (mask.at<uchar>(row, col) != 0)
            posBuffer[3 * index + 2] = depthmap.at<uchar>(row, col) * depth;
        index++;
    }
}
vertexbuffer.unmap();
vertexbuffer.release();
0 голосов
/ 15 октября 2018

Этот вызов здесь

GLfloat* PosBuffer = (GLfloat*) (m_vboPos.map(QOpenGLBuffer::WriteOnly));

вызовет внутренний вызов glMapBuffer, что означает, что он просто отображает содержимое буфера в адресное пространство вашего процесса (см. также OpenGL Wiki по отображению объектов буфера .

Поскольку вы отображаете его только для записи, вы можете просто перезаписывать каждый бит буфера, как считаете нужным.нужно использовать memcpy, вы можете просто использовать любые средства для записи в память, например, вы можете напрямую сделать

PosBuffer[3*vertex_id + 2] = 42.0f; // assuming 3 floats per vertex

Я не думаю, что с помощью memcpy можно изменить только 1 значение с плавающей запятойв каждой рассматриваемой вершине было бы очень эффективно (у меня есть несколько миллионов вершин в VBO).

Да, выполнение миллиона отдельных memcpy() вызовов по 4 байта каждый не будет хорошей идеей.Современный компилятор может на самом деле встроить его, поэтому он может быть эквивалентен только отдельным назначениям, но вы также можете выполнять назначения напрямую, поскольку memcpy здесь ничего вам не дает.

Однако этоне понятно что завлияние на производительность всего этого.glMapBuffer может вернуть указатель на

  • некоторую локальную копию VBO в системной памяти, и позже придется копировать содержимое в графический процессор.Поскольку он не знает, какие значения вы изменили, а какие нет, ему, возможно, придется повторно передать весь буфер.
  • некоторая системная память в области GART, которая отображается на графическом процессоре, поэтому графический процессор будет напрямуюдоступ к этой памяти при чтении из буфера.
  • некоторая область I / O, отображаемая в VRAM.В этом случае поведение кэширования области памяти может существенно отличаться, и изменение 4 байтов в каждом 12-байтовом блоке может оказаться не самым идеальным подходом.Простое повторное копирование всего подблока, так как один большой мусор может повысить производительность.

Само сопоставление также не бесплатно, оно требует изменения таблиц страниц, и драйвер GL, возможно, придетсясинхронизировать его потоки или, в худшем случае, синхронизировать с графическим процессором (чтобы вы не могли перезаписывать данные, которые графический процессор все еще использует для предыдущего вызова отрисовки, который все еще находится в полете).

иногда яхотите изменить только значение z диапазона вершин между значениями (x1, y1) и (x2, y2) - соответствующие вершины строго следуют друг за другом

Таким образом, у вас есть непрерывный подпунктобласть буфера, который вы хотите изменить.Я бы порекомендовал взглянуть на две альтернативы:

  1. Используйте glMapBufferRange (если доступно в вашей версии OpenGL), чтобы отобразить только интересующий вас регион.

  2. Полностью забудьте о отображении буфера и попробуйте glBufferSubData().Не индивидуально для каждого z компонента каждой вершины, но как один большой мусор для всего диапазона модифицированных вершин.Это будет означать, что у вас есть локальная копия содержимого буфера в вашей памяти где-то, просто обновите и отправьте результаты в GL.

Какой вариант лучше будет зависеть от многихразличных факторов, и я бы не стал исключать один из них без сопоставления в реальном сценарии с реальными реализациями, которые вас интересуют.Также взгляните на общие стратегии для Потоковых объектов буфера в OpenGL . постоянно отображаемый буфер может быть или не быть хорошим вариантом для вашего варианта использования.

...