Почему функции OpenGL ES нельзя вызывать из другого потока - PullRequest
10 голосов
/ 24 октября 2011

Я экспериментирую с написанием небольшого игрового движка для Android с использованием OpenGL ES. Я создал игровую ветку, которая обновляет игровые объекты, а GLThread рисует сцену. Я читал, что вам нужно загрузить текстуры в метод onSurfaceCreated GLSurfaceView. Я следую за этим, но для какой-то цели отладки я пытался загрузить текстуры из моего игрового потока вместо GLThread. Я не получил никаких ошибок и текстуры не отображаются на экране. Я провел весь день, пытаясь выяснить проблему, и, наконец, я прочитал ниже здесь

«Просто используйте только OpenGL в основном потоке». Очень важно. Вы не можете вызвать в своем игровом движке (который может быть в другом потоке) функцию загрузки текстуры, которая не синхронизирована с gl-потоком. Установите там флаг, сигнализирующий вашей gl-thread о загрузке новой текстуры (например, вы можете поместить функцию в OnDrawFrame (GL gl), которая проверяет, должна ли быть загружена новая текстура.

Я изменю свой код так, чтобы текстуры загружались из GL Thread. Я просто не мог понять, почему это так? Почему функции OpenGL не работают из другого потока?

Я знаю, как создавать потоки, но я не знаю, что означает синхронизация. Вышеприведенный фрагмент упоминает: «Вы не можете вызвать в игровом движке (который может быть в другом потоке) функцию загрузки текстуры, которая не синхронизирована с gl-потоком». Поэтому я думаю, что моя игровая нить может не синхронизироваться с GL-нитью. Можно ли создать другой поток, синхронизированный с потоком GL, чтобы из него можно было вызывать функции GL? Чему я должен научиться в потоке, чтобы понять эти понятия?

1 Ответ

8 голосов
/ 25 октября 2011

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

glVertexPointer(... supply vertex positions ...)
glTexCoordPointer(... provide texture positions ...)
/* and supply a few other pointers, maybe bind a texture */

glDrawArrays(... draw some geometry ...)

Таким образом, последний вызов обеспечивает предсказуемые результаты только в контексте предыдущих вызовов. Если вы допустите, чтобы этот бит кода был приостановлен после, скажем, glVertexPointer, другого потока, включающегося и выполняющего почти ту же последовательность для рисования своей геометрии, а затем этого кода, он будет рисовать с совершенно неправильной геометрией, возможно даже вызывает доступ к памяти за пределами допустимого диапазона, если некоторые из замененных массивов меньше оригиналов.

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

Поэтому привязка контекста к одному потоку не является большой проблемой. Планирование проблемы в самом OpenGL также было бы непростым, поскольку одна из причин того, что контексты OpenGL создаются, управляются и утилизируются в зависимости от конкретной ОС, заключается в том, что некоторые ОС должны обрабатывать эти вещи радикально по-другому, чем другие. или способны предложить лучшие решения, предоставляя свои собственные уникальные решения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...