线程纹理加载进程为android opengl游戏

时间:2011-06-11 20:15:14

标签: android opengl-es textures

我有大量JPG格式的纹理。 我需要在实际绘图开始之前在opengl内存中预加载它们。 我问了一个问题,我被告知这样做的方法是将JPEG解包和glTexImage2D(...)调用分开到另一个线程。 问题是我不太清楚如何做到这一点。

执行glTexImage2D所需的OpenGL(处理程序?)仅在GLSurfaceView.Renderer的onSurfaceCreated和OnDrawFrame方法中可用。

我无法解压缩所有纹理,然后在onSurfaceCreated(...)中将它们加载到opnegl中, 因为它们可能不适合有限的vm内存(20-40MB?)

这意味着我必须逐个解包并加载它们,但在这种情况下我无法获得一个opengl指针。

请有人,请给我以及为opengl游戏加载纹理的示例吗?

它必须是一些典型的程序,我无法在任何地方获得任何信息。

5 个答案:

答案 0 :(得分:10)

如'OpenGLES preloading textures in other thread'中所述,有两个独立的步骤:位图创建和位图上传。在大多数情况下,只需在辅助线程上创建位图就可以了 - 这非常简单。

如果您在上传纹理时遇到帧丢失,请从后台线程调用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创建了上述代码段的工作演示。

此解决方案基于互联网上的许多来源。最有影响力的是这三个:

答案 1 :(得分:2)

您只需拥有上传例程的主线程,即可访问OpenGL并调用glTexImage2D。另一个线程将图像从文件加载(并解码)到内存。当辅助线程加载下一个图像时,主线程将先前加载的图像上载到纹理中。因此,您只需要两个图像的内存,即当前从文件加载的图像和当前上载到GL中的图像(之前加载的图像)。当然你需要一些同步,以防止加载器线程覆盖内存,主线程当前发送给GL并防止主线程发送未完成的数据。

答案 2 :(得分:2)

“必须有一种方法可以在初始化函数之外调用GL函数。” - 是的只需将指针复制到gl并在任何地方使用它。

“确保只在主线程中使用OpenGL。”很重要。你不能在你的游戏引擎(可能在另一个线程中)中调用一个与gl-thread不同步的纹理加载函数。设置一个标志来指示你的gl线程加载一个新纹理(例如,你可以在OnDrawFrame(GL gl)中放置一个函数来检查是否必须加载一个新的纹理。

答案 3 :(得分:0)

为此找到了一个解决方案,这实际上非常简单:在加载位图(在单独的线程中)后,将其存储在实例变量中,并在draw方法中检查它是否已初始化,如果是的,加载纹理。像这样:

if (bitmap != null && textureId == -1) {
    initTexture(gl, bitmap);
}

答案 4 :(得分:0)

要添加到Rodja's answer,如果您需要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),因为默认配置选择器也会使用它。

这基于Attribute list in eglCreateContext