Процесс загрузки текстур потоков для Android OpenGL - PullRequest
9 голосов
/ 12 июня 2011

У меня большое количество текстур в формате JPG. И мне нужно предварительно загрузить их в память opengl, прежде чем начнется фактическое рисование. Я задал вопрос, и мне сказали, что способ сделать это - отделить распаковку JPEG от вызовов glTexImage2D (...) в другой поток. Проблема в том, что я не совсем уверен, как это сделать.

OpenGL (обработчик?), Необходимый для выполнения glTexImage2D, доступен только в методах GLSurfaceView.Renderer onSurfaceCreated и OnDrawFrame.

Я не могу распаковать все свои текстуры и затем в onSurfaceCreated (...) загрузить их в opnegl, потому что они, вероятно, не поместятся в памяти ограниченного vm (20-40 МБ?)

Это означает, что я должен распаковать и загрузить их один за другим, но в этом случае я не могу получить указатель opengl.

Может ли кто-нибудь дать мне пример загрузки потоков текстур для игры opengl?

Это должна быть какая-то типичная процедура, и я нигде не могу получить информацию.

Ответы [ 5 ]

10 голосов
/ 02 ноября 2013

Как объяснено в ' Предварительная загрузка текстур OpenGLES в другой поток ', существует два отдельных шага: создание растрового изображения и выгрузка растрового изображения. В большинстве случаев у вас все будет в порядке, просто создавая растровое изображение во вторичном потоке - что довольно просто.

Если вы испытываете пропадание кадров при загрузке текстур , вызовите texImage2D из фонового потока. Для этого вам нужно создать новый контекст OpenGL, который делится своими текстурами с вашим потоком рендеринга, потому что каждому потоку нужен свой собственный контекст OpenGL.

EGLContext textureContext = egl.eglCreateContext(display, eglConfig, renderContext, null);

Получить параметры для eglCreateContext немного сложно. Вам нужно использовать setEGLContextFactory на вашем SurfaceView, чтобы подключиться к созданию EGLContext:

@Override
public EGLContext createContext(final EGL10 egl, final EGLDisplay display, final EGLConfig eglConfig) {
     EGLContext renderContext = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, null);

     // create your texture context here

     return renderContext;
}

Тогда вы готовы запустить поток загрузки текстуры:

public void run() {
    int pbufferAttribs[] = { EGL10.EGL_WIDTH, 1, EGL10.EGL_HEIGHT, 1, EGL14.EGL_TEXTURE_TARGET,
            EGL14.EGL_NO_TEXTURE, EGL14.EGL_TEXTURE_FORMAT, EGL14.EGL_NO_TEXTURE,
            EGL10.EGL_NONE };

    EGLSurface localSurface = egl.eglCreatePbufferSurface(display, eglConfig, pbufferAttribs);
    egl.eglMakeCurrent(display, localSurface, localSurface, textureContext);

    int textureId = loadTexture(R.drawable.waterfalls);

    // here you can pass the textureId to your 
    // render thread to be used with glBindTexture
}

Я создал рабочую демонстрацию приведенных выше фрагментов кода в https://github.com/perpetual-mobile/SharedGLContextsTest.

Это решение основано на многих источниках в Интернете. Самые влиятельные, где эти три:

2 голосов
/ 07 октября 2011

"Должен быть способ вызова функций GL вне функции инициализации."- Да.Просто скопируйте указатель на gl и используйте его где угодно.

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

2 голосов
/ 12 июня 2011

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

0 голосов
/ 03 апреля 2014

Чтобы добавить к ответ Роджи , если вы хотите контекст OpenGL ES 2.0, используйте следующий текст для создания контекста:

final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
int[] contextAttributes = 
    { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
EGLContext renderContext = egl.eglCreateContext(
    display, config, EGL10.EGL_NO_CONTEXT, contextAttributes);

Вам по-прежнему необходимо также вызывать setEGLContextClientVersion(2), так как он также используется в настройке по умолчанию.

Это основано на Списке атрибутов в eglCreateContext

0 голосов
/ 24 февраля 2013

Нашел решение для этого, что на самом деле очень просто: после загрузки растрового изображения (в отдельном потоке), сохранения его в переменной экземпляра и в методе draw вы проверяете, инициализирован ли он, если да, загрузить текстуру. Как то так:

if (bitmap != null && textureId == -1) {
    initTexture(gl, bitmap);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...