C++ OpenGL Threaded Terrain Сбой

введите здесь описание изображения

Какова цель:

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

Проблема(-ы): когда я не запускал потоки, возникало небольшое отставание из-за других систем в движке. Основная причина лагов - загрузка и удаление ландшафта в QuadTree (я даже не уверен, что это оптимальный способ сделать это). Теперь рендеринг происходит очень быстро и вроде не лагает. Когда камера стоит на месте, игра идет нормально. Я оставил игру включенной на час, и никаких сбоев не было обнаружено.

Когда ландшафт загружается, он использует несколько переменных, которые использует код рендеринга. А именно привязка буферов -

    glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
    glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), &vertices[0], GL_STATIC_DRAW);

    glBindBuffer(GL_ARRAY_BUFFER, normalbuffer);
    glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(glm::vec3), &normals[0], GL_STATIC_DRAW);

    glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
    glBufferData(GL_ARRAY_BUFFER, uvs.size() * sizeof(glm::vec2), &uvs[0], GL_STATIC_DRAW);

Я считаю, что доступ к этой переменной осуществляется одновременно с другим потоком. Что вызывает сбой. Как это исправить? Я пытался использовать мьютексы, но это, похоже, не работает. Где я могу заблокировать и разблокировать мьютекс, чтобы исправить это?

Другая переменная, которая, похоже, вызывает ту же ошибку, — «IsLeaf».

Другой сбой (std::badAlloc) происходит после загрузки большого количества ландшафта. Несмотря на то, что его убирают. Я предполагаю, что это связано с моим кодом удаления, но я не знаю, что не так.

В настоящее время я добавляю и удаляю плитки, проверяя диапазон с камеры и удаляя/создавая плитку. Я хочу отобразить плитку, на которой я нахожусь, и те, что вокруг нее. Однако это не работает при переходе с одной из 4 основных плиток. Создание с использованием диапазона не работает, так как это диапазон до центра большой плитки, а не меньших. Я также пытался удалять всю карту каждые несколько секунд, и это тоже работает, но с большей задержкой. Есть ли лучший способ сделать созидание и разрушение?

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

Если у вас есть идеи, как исправить одну из этих ошибок, мы будем очень признательны.

Код (слишком много для загрузки сюда)

http://pastebin.com/MwXaymG0

http://pastebin.com/2tRbqtEB


person Vangoule    schedule 18.08.2013    source источник


Ответы (1)


Контекст OpenGL может быть привязан только к одному потоку за раз (через wglMakeCurrent() в Windows).

Поэтому вам не следует использовать функции gl* между потоками, даже если вы используете мьютексы для защиты доступа к определенным переменным в памяти, вызовы не будут выполнены.

Я бы предложил переместить ваши вызовы gl * в поток рендеринга, однако иметь такие вещи, как загрузка ландшафта, расчеты усеченной пирамиды, отсечение и т. Д., В другом потоке. Поток рендеринга просто должен проверить, есть ли у объекта новые данные, а затем выполнить соответствующие вызовы GL как часть его обновления/рендеринга.

person Andy Esser    schedule 19.08.2013
comment
В дополнение к этому: отправка команд рисования из нескольких потоков для работы с одним и тем же фреймбуфером фактически замедлит работу. Кадровый буфер является взаимоисключающим ресурсом. И когда к нему обращается несколько потоков, драйвер и графический процессор будут тратить много времени на переключение между контекстами. Но что еще хуже, вводится множество неявных точек синхронизации, которые очищают конвейер, убивая пропускную способность. Короче говоря Не делайте код рендеринга многопоточным! ГП будет внутренне распараллеливать все красиво вплоть до уровня фрагмента. Ничего не нужно делать вручную в этом отношении - person datenwolf; 19.08.2013
comment
Определенно согласен с @datenwolf. Все вызовы рендеринга должны быть в одном потоке. Нет никакого вреда в загрузке данных текстуры/модели в системную память в потоке A, а затем в потоке рендеринга (поток B), фактически передающем их драйверу через вызовы gl и т. д. - person Andy Esser; 19.08.2013
comment
Вы можете сопоставить VBO/PBO в потоке OpenGL (glMapBuffer) и заставить потоки ввода-вывода считывать данные полезной нагрузки непосредственно в память OpenGL, избегая одной дополнительной копии. - person datenwolf; 19.08.2013
comment
Это очень хороший момент, т.е. выделить место в потоке рендеринга, а затем прочитать его в это выделенное пространство памяти в дополнительном потоке? Я не думал об этом раньше. Это будет хороший прирост производительности. Ваше здоровье - person Andy Esser; 19.08.2013
comment
Итак, создание буферов также должно происходить в потоке рендеринга? В настоящее время сам рендеринг выполняется только в потоке рендеринга, поэтому инициализация выполняется в потоке A. Было бы лучше сделать рендеринг в потоке A, а загрузку физики/ландшафта — в потоке B? - person Vangoule; 19.08.2013
comment
Вы должны ограничить все вызовы gl* одним назначенным потоком (это то, что я имел в виду под потоком рендеринга). Загрузка физики/ландшафта может быть в отдельном потоке, а затем аудио в еще одном потоке. - person Andy Esser; 19.08.2013