OpenGL VBO в нескольких потоках - PullRequest
5 голосов
/ 18 января 2012

Я занимаюсь разработкой программы на C ++ / OpenGL, которая рисует ландшафт всего мира.У меня есть база данных высоты над уровнем моря, хранящаяся в виде плиток.Каждый раз, когда я запускаю программу, плитка загружается.Затем, когда человек двигается, должна загружаться другая плитка, это происходит не каждый кадр, возможно, раз в 5 минут.

Я загружаю исходную плитку в память видеокарты:

glBindBufferARB(GL_ARRAY_BUFFER_ARB, VertexBuffer[idx]);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, VBOsz * 3 * sizeof(float), tile_data, GL_STATIC_DRAW_ARB);

... Есть буферы нормали, цвета и индекса

И я их рисую:

glBindBufferARB(GL_ARRAY_BUFFER_ARB, VertexBuffer[idx]);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, VBOsz * 3 * sizeof(float), tile_data, GL_STATIC_DRAW_ARB);


glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);

glBindBufferARB(GL_ARRAY_BUFFER_ARB, VertexBuffer[idx]);
glVertexPointer(3, GL_FLOAT, 0, BUFFER_OFFSET(0));

...

glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, IndexBuffer[idx]);
glDrawElements(GL_TRIANGLES, IndexBuffersz, GL_UNSIGNED_INT, BUFFER_OFFSET(0));

glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);

Так как я хочу, чтобы программа былаКак можно более гладко, я не могу вычислить вершину + цвет + нормальные + другие текстуры в том же кадре, поскольку создание плитки занимает около 20 секунд.

Поэтому я решил создать поток загрузчика, который бы проверялкогда новая плитка должна быть загружена и затем загружена.Когда все это будет сделано, он должен просто поменять VBO (отсюда [idx].

). Так что для потока загрузчика я знаю, что мне нужен второй контекст OpenGL, я создал его и делюсь спискамимежду ними. Идея работает, но в потоке загрузчика, когда я отправляю новые данные VBO, мне нужна эта функция: wglMakeCurrent

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

Есть ли у вас какие-либо идеи по поводу решения? Нужно ли менять концепцию?

Я использую OpenGL 2.1. Решит ли проблема обновление до OpenGL 3?

Ответы [ 3 ]

7 голосов
/ 18 января 2012

Это действительно не так сложно.

Вы просто создаете два буферных объекта: один, который вы используете для текущего рендеринга, и другой, который вы будете использовать в будущем. Когда неиспользуемый буфер заполнен данными, поток рендеринга переключается на рендеринг с этого. Ранее использованный буфер становится неиспользованным.

Вы можете загрузить данные одним из двух способов. Один из способов - создать поток данных, который поток рендеринга будет загружать в буферный объект с помощью glBufferData. Очевидно, что для этого потребуется некоторая синхронизация, но вам нужен именно такой код синхронизации: ваш поток рендеринга должен быть в конечном итоге проинформирован о готовности данных, чтобы он мог рендериться с новыми данными.

Другой способ - сообщить потоку рендеринга, что данные должны начать генерироваться. В этот момент он отобразит неиспользуемый буферный объект и передаст сопоставленный указатель потоку создания данных. Этот поток будет генерировать данные непосредственно в этот сопоставленный указатель. Когда он заканчивается, он сообщает потоку рендеринга, который разархивирует буфер и затем отрендерит с данными.

Ни один из методов не требует нескольких контекстов или потоков через код OpenGL.

Обратите внимание, что производительность лучше всего будет обслуживать , а не , делая буфера все больше и меньше. Выберите размер и придерживайтесь его; Вы не хотите изменять размер буферов с помощью glBufferData.

3 голосов
/ 18 января 2012

У меня уже есть ответ для такого рода задач.

В двух словах вы создаете два общих контекста. Затем, как предлагает Дэймон , сделайте контексты текущими в их собственном потоке, только один раз в начале выполнения потока. Два контекста будут актуальными одновременно в разных потоках (один поток, один контекст).

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

По сути, вам не нужно беспокоиться о наведении указателя на приложение и блокировании потока рендеринга для загрузки данных; но за счет создания другого контекста. Позвольте драйверу синхронизировать состояния контекста: если он может плавно выполнять эти операции, ваше приложение извлечет из этого пользу; по крайней мере, ваш код будет чище.

Другие мои работы по теме:

2 голосов
/ 18 января 2012

Вы должны вызывать wglMakeCurrent только один раз в каждом потоке. Это работает надежно, это то, что я делаю (хотя с OpenGL 3.3). Это отмечает один контекст, принадлежащий одному потоку. Так будет до тех пор, пока вы не скажете OpenGL по-другому, поэтому сделайте это один раз в начале и забудьте (на самом деле, вам вообще не нужно вызывать его, если вы создаете контексты в их соответствующих потоках, используя их, но все равно делаете это просто для того, чтобы быть на 100% безопасным, также я предпочитаю создавать все контексты перед запуском, это не так грязно ...).

Кстати, вам не нужно беспокоиться о указателе функции, просто используйте тот же, который вы использовали в потоке рендеринга (при условии, что вы правильно его инициализировали).
Технически, недопустимо использовать указатель на функцию из другого контекста. Однако WGL любезно гарантирует (скрытый мелким шрифтом!), Что указатели функций идентичны для всех контекстов, имеющих одинаковый формат пикселей. Таким образом, вы готовы идти.

Альтернативой, которая работает с одним контекстом, является glMapBuffer в потоке рендеринга и передача указателя в рабочий поток. Затем, по завершении (например, сигнализируя семафор), glUnmapBuffer снова в потоке рендеринга.
Некоторые люди предпочитают это, так как это не включает в себя жонглирование контекста и, вероятно, лучше работает на некоторых старых глючных драйверах. Мне это не нравится из-за необходимости дополнительной синхронизации. Это вопрос вкуса, тот же эффект.

...