2012-01-18 4 views
4

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

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

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

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

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

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

+5

Пожалуйста, прекратите использование формы АРБ-расширения объектов буфера. Они были основной функциональностью почти десять лет; больше нет оправдания. –

ответ

2

У меня уже есть answer для выполнения таких задач.

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

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

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

Другие материалы шахты по теме:

+0

Спасибо, это именно то, как я его настроил: вторичный поток, который выполняет эту работу, с общими контекстами. Для некоторых приложений блокировка рендеринга не является вариантом. – Tibi

2

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

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

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

+0

Благодарим за помощь. Добавление wglMakeCurrent в качестве начала потока рендеринга заставляет его работать. Однако, с моей первой попытки, я заставил замолчать компьютер. Я не уверен в точной строке, но это произошло при вызове wglMakeCurrent в потоке загрузчика или сразу после этого: glBindBuffer и glBufferData. Теперь это работает нормально. Является ли это плохой кодировкой для этого? – Tibi

+0

@Tibi: Это больше похоже на неудачу.Какие шаги вы предприняли, чтобы убедиться, что вы не используете один и тот же объект в обоих потоках одновременно? –

+0

@Nicol: В потоке загрузчика у меня есть переменная, только после того, как она закончит выполнение своей работы, я установил переменную в true. Затем поток рендеринга выполняет фактический «флип» буферов. После загрузки данных загрузочный поток не выполняет никакой работы OpenGL. – Tibi

6

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

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

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

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

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

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

+0

Я пробовал оба метода, но glBufferData занимает не менее 200 миллисекунд, а отображение/размена также занимает 100 миллисекунд. В моем приложении у меня не может быть этого, потому что он заикается. Размер буфера не изменится. – Tibi

+1

@Tibi: Вы лжете реализации OpenGL, используя 'STATIC_DRAW', когда ваши данные фактически не статичны *? –

+0

Я не изменяю данные VBO, я использую двойную буферизацию. Поэтому я думаю, что STATIC_DRAW в порядке, не так ли? – Tibi