错误日志非常清晰地指出了基本问题:
没有当前上下文的OpenGLES API调用(每个线程记录一次)
在进行任何OpenGL调用之前,包括你的
glGetIntegerv()
调用,你需要在你的线程中拥有一个当前的OpenGL上下文。这一点始终如此,但似乎在棒棒糖版本之前,在框架中创建了一个OpenGL上下文,并且在调用应用程序代码时有时(总是?)处于当前状态。
我不相信这曾经被记录或者是期望的行为。如果想要进行OpenGL调用,应用程序始终应该明确地创建并使其成为当前上下文。而且似乎在棒棒糖版本中更加严格执行此规定。
创建OpenGL上下文有两种主要方法:
- 创建一个
GLSurfaceView
(文档)。这是最简单和最方便的方法,但只有在计划对显示器进行OpenGL渲染时才有意义。
- 使用
EGL14
(文档)。这提供了一个更低级别的接口,允许您完成OpenGL调用所需的必要设置,而无需创建视图或对显示器进行渲染。
GLSurfaceView
方法在各处都有广泛的文档、示例和教程。因此,我将重点介绍EGL方法。
使用EGL14 (API级别17)
下面的代码假定你关心ES 2.0,对于其他版本的ES,一些属性值需要进行调整。
在文件开始处导入
EGL14
类和几个相关的类。
import android.opengl.EGL14;
import android.opengl.EGLConfig;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
import android.opengl.GLES20;
获取默认显示器并进行初始化。如果你需要处理可能有多个显示设备的情况,这可能会更加复杂,但对于普通手机/平板电脑来说已经足够:
EGLDisplay dpy = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
int[] vers = new int[2];
EGL14.eglInitialize(dpy, vers, 0, vers, 1);
接下来,我们需要找到一个配置文件。由于我们不会将此上下文用于渲染,因此确切的属性并不是非常关键:
int[] configAttr = {
EGL14.EGL_COLOR_BUFFER_TYPE, EGL14.EGL_RGB_BUFFER,
EGL14.EGL_LEVEL, 0,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT,
EGL14.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] numConfig = new int[1];
EGL14.eglChooseConfig(dpy, configAttr, 0,
configs, 0, 1, numConfig, 0);
if (numConfig[0] == 0) {
}
EGLConfig config = configs[0];
为了使上下文在稍后使用时处于当前状态,您需要一个渲染表面,即使您实际上没有计划进行渲染。为满足此要求,请创建一个小的离屏(Pbuffer)表面:
int[] surfAttr = {
EGL14.EGL_WIDTH, 64,
EGL14.EGL_HEIGHT, 64,
EGL14.EGL_NONE
};
EGLSurface surf = EGL14.eglCreatePbufferSurface(dpy, config, surfAttr, 0);
接下来,创建上下文:
int[] ctxAttrib = {
EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
EGL14.EGL_NONE
};
EGLContext ctx = EGL14.eglCreateContext(dpy, config, EGL14.EGL_NO_CONTEXT, ctxAttrib, 0);
现在准备更新上下文:
EGL14.eglMakeCurrent(dpy, surf, surf, ctx);
如果上述所有步骤都成功(略去错误检查),现在就可以进行OpenGL调用:
int[] maxSize = new int[1];
GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxSize, 0);
完成后,您可以拆除所有内容:
EGL14.eglMakeCurrent(dpy, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE,
EGL14.EGL_NO_CONTEXT);
EGL14.eglDestroySurface(dpy, surf);
EGL14.eglDestroyContext(dpy, ctx);
EGL14.eglTerminate(dpy);
使用 EGL10(API 级别 1)
如果您需要适用于早期级别的内容,可以使用 EGL10
(文档) 代替 EGL14,EGL10 自 API 级别 1 就已可用。如下所示是针对 1.0 改编后的代码:
import android.opengl.GLES10;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
EGL10 egl = (EGL10)EGLContext.getEGL();
EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
int[] vers = new int[2];
egl.eglInitialize(dpy, vers);
int[] configAttr = {
EGL10.EGL_COLOR_BUFFER_TYPE, EGL10.EGL_RGB_BUFFER,
EGL10.EGL_LEVEL, 0,
EGL10.EGL_SURFACE_TYPE, EGL10.EGL_PBUFFER_BIT,
EGL10.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] numConfig = new int[1];
egl.eglChooseConfig(dpy, configAttr, configs, 1, numConfig);
if (numConfig[0] == 0) {
}
EGLConfig config = configs[0];
int[] surfAttr = {
EGL10.EGL_WIDTH, 64,
EGL10.EGL_HEIGHT, 64,
EGL10.EGL_NONE
};
EGLSurface surf = egl.eglCreatePbufferSurface(dpy, config, surfAttr);
final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
int[] ctxAttrib = {
EGL_CONTEXT_CLIENT_VERSION, 1,
EGL10.EGL_NONE
};
EGLContext ctx = egl.eglCreateContext(dpy, config, EGL10.EGL_NO_CONTEXT, ctxAttrib);
egl.eglMakeCurrent(dpy, surf, surf, ctx);
int[] maxSize = new int[1];
GLES10.glGetIntegerv(GLES10.GL_MAX_TEXTURE_SIZE, maxSize, 0);
egl.eglMakeCurrent(dpy, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_CONTEXT);
egl.eglDestroySurface(dpy, surf);
egl.eglDestroyContext(dpy, ctx);
egl.eglTerminate(dpy);
请注意,此代码版本使用的是ES 1.x上下文。所报告的最大纹理尺寸可能与ES 2.0不同。