Hi Romain,
I am wondering about the Thread.sleep() call in the rendering thread's run
loop. Is that technically necessary? If so, may I ask why? And if so, then
how would one implement a high-framerate OpenGL game (for instance) using
TextureView?
What prompted these questions is that I have run into a few devices where
this code 'crashes' with (randomly) one of the two following errors. Are
you able to comment on this?
1. queueBuffer: slot 2 is current!
2. dequeueBuffer: buffer 0 is both FREE and current!
Many thanks,
-Nathan Morse
On Wednesday, November 23, 2011 9:17:28 AM UTC-8, Romain Guy (Google) wrote:
>
> GLSurfaceView handles GL setup for you, which TextureView will not do. A
> TextureView can be used as the native window when you create an EGL
> surface. Here is an example (the interesting part is the call
> to eglCreateWindowSurface()):
>
> @Override
> public void onSurfaceTextureAvailable(SurfaceTexture surface, int
> width, int height) {
> mRenderThread = new RenderThread(getResources(), surface);
> mRenderThread.start();
> }
>
> private static class RenderThread extends Thread {
> private static final String LOG_TAG = "GLTextureView";
>
> static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
> static final int EGL_OPENGL_ES2_BIT = 4;
>
> private volatile boolean mFinished;
>
> private final Resources mResources;
> private final SurfaceTexture mSurface;
>
> private EGL10 mEgl;
> private EGLDisplay mEglDisplay;
> private EGLConfig mEglConfig;
> private EGLContext mEglContext;
> private EGLSurface mEglSurface;
> private GL mGL;
>
> RenderThread(Resources resources, SurfaceTexture surface) {
> mResources = resources;
> mSurface = surface;
> }
>
> private static final String sSimpleVS =
> "attribute vec4 position;\n" +
> "attribute vec2 texCoords;\n" +
> "varying vec2 outTexCoords;\n" +
> "\nvoid main(void) {\n" +
> " outTexCoords = texCoords;\n" +
> " gl_Position = position;\n" +
> "}\n\n";
> private static final String sSimpleFS =
> "precision mediump float;\n\n" +
> "varying vec2 outTexCoords;\n" +
> "uniform sampler2D texture;\n" +
> "\nvoid main(void) {\n" +
> " gl_FragColor = texture2D(texture, outTexCoords);\n" +
> "}\n\n";
>
> private static final int FLOAT_SIZE_BYTES = 4;
> private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 *
> FLOAT_SIZE_BYTES;
> private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
> private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
> private final float[] mTriangleVerticesData = {
> // X, Y, Z, U, V
> -1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
> 1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
> -1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
> 1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
> };
>
> @Override
> public void run() {
> initGL();
>
> FloatBuffer triangleVertices =
> ByteBuffer.allocateDirect(mTriangleVerticesData.length
> *
> FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();
> triangleVertices.put(mTriangleVerticesData).position(0);
>
> int texture = loadTexture(R.drawable.large_photo);
> int program = buildProgram(sSimpleVS, sSimpleFS);
>
> int attribPosition = glGetAttribLocation(program, "position");
> checkGlError();
>
> int attribTexCoords = glGetAttribLocation(program,
> "texCoords");
> checkGlError();
>
> int uniformTexture = glGetUniformLocation(program, "texture");
> checkGlError();
>
> glBindTexture(GL_TEXTURE_2D, texture);
> checkGlError();
>
> glUseProgram(program);
> checkGlError();
>
> glEnableVertexAttribArray(attribPosition);
> checkGlError();
>
> glEnableVertexAttribArray(attribTexCoords);
> checkGlError();
>
> glUniform1i(uniformTexture, texture);
> checkGlError();
>
> while (!mFinished) {
> checkCurrent();
>
> glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
> checkGlError();
>
> glClear(GL_COLOR_BUFFER_BIT);
> checkGlError();
>
> // drawQuad
>
> triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
> glVertexAttribPointer(attribPosition, 3, GL_FLOAT, false,
> TRIANGLE_VERTICES_DATA_STRIDE_BYTES,
> triangleVertices);
>
>
> triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
> glVertexAttribPointer(attribTexCoords, 3, GL_FLOAT, false,
> TRIANGLE_VERTICES_DATA_STRIDE_BYTES,
> triangleVertices);
>
> glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
>
> if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
> throw new RuntimeException("Cannot swap buffers");
> }
> checkEglError();
>
> try {
> Thread.sleep(2000);
> } catch (InterruptedException e) {
> // Ignore
> }
> }
>
> finishGL();
> }
>
> private int loadTexture(int resource) {
> int[] textures = new int[1];
>
> glActiveTexture(GL_TEXTURE0);
> glGenTextures(1, textures, 0);
> checkGlError();
>
> int texture = textures[0];
> glBindTexture(GL_TEXTURE_2D, texture);
> checkGlError();
>
> glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
> GL_LINEAR);
> glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
> GL_LINEAR);
>
> glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
> GL_CLAMP_TO_EDGE);
> glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
> GL_CLAMP_TO_EDGE);
>
> Bitmap bitmap = BitmapFactory.decodeResource(mResources,
> resource);
>
> GLUtils.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap,
> GL_UNSIGNED_BYTE, 0);
> checkGlError();
>
> bitmap.recycle();
>
> return texture;
> }
>
> private int buildProgram(String vertex, String fragment) {
> int vertexShader = buildShader(vertex, GL_VERTEX_SHADER);
> if (vertexShader == 0) return 0;
>
> int fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
> if (fragmentShader == 0) return 0;
>
> int program = glCreateProgram();
> glAttachShader(program, vertexShader);
> checkGlError();
>
> glAttachShader(program, fragmentShader);
> checkGlError();
>
> glLinkProgram(program);
> checkGlError();
>
> int[] status = new int[1];
> glGetProgramiv(program, GL_LINK_STATUS, status, 0);
> if (status[0] != GL_TRUE) {
> String error = glGetProgramInfoLog(program);
> Log.d(LOG_TAG, "Error while linking program:\n" + error);
> glDeleteShader(vertexShader);
> glDeleteShader(fragmentShader);
> glDeleteProgram(program);
> return 0;
> }
>
> return program;
> }
>
> private int buildShader(String source, int type) {
> int shader = glCreateShader(type);
>
> glShaderSource(shader, source);
> checkGlError();
>
> glCompileShader(shader);
> checkGlError();
>
> int[] status = new int[1];
> glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0);
> if (status[0] != GL_TRUE) {
> String error = glGetShaderInfoLog(shader);
> Log.d(LOG_TAG, "Error while compiling shader:\n" + error);
> glDeleteShader(shader);
> return 0;
> }
>
> return shader;
> }
>
> private void checkEglError() {
> int error = mEgl.eglGetError();
> if (error != EGL10.EGL_SUCCESS) {
> Log.w(LOG_TAG, "EGL error = 0x" +
> Integer.toHexString(error));
> }
> }
>
> private void checkGlError() {
> int error = glGetError();
> if (error != GL_NO_ERROR) {
> Log.w(LOG_TAG, "GL error = 0x" +
> Integer.toHexString(error));
> }
> }
>
> private void finishGL() {
> mEgl.eglDestroyContext(mEglDisplay, mEglContext);
> mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
> }
>
> private void checkCurrent() {
> if (!mEglContext.equals(mEgl.eglGetCurrentContext()) ||
>
> !mEglSurface.equals(mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) {
> if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface,
> mEglSurface, mEglContext)) {
> throw new RuntimeException("eglMakeCurrent failed "
> +
> GLUtils.getEGLErrorString(mEgl.eglGetError()));
> }
> }
> }
>
> private void initGL() {
> mEgl = (EGL10) EGLContext.getEGL();
>
> mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
> if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
> throw new RuntimeException("eglGetDisplay failed "
> + GLUtils.getEGLErrorString(mEgl.eglGetError()));
> }
>
> int[] version = new int[2];
> if (!mEgl.eglInitialize(mEglDisplay, version)) {
> throw new RuntimeException("eglInitialize failed " +
> GLUtils.getEGLErrorString(mEgl.eglGetError()));
> }
>
> mEglConfig = chooseEglConfig();
> if (mEglConfig == null) {
> throw new RuntimeException("eglConfig not initialized");
> }
>
> mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
>
> mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay,
> mEglConfig, mSurface, null);
>
> if (mEglSurface == null || mEglSurface ==
> EGL10.EGL_NO_SURFACE) {
> int error = mEgl.eglGetError();
> if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
> Log.e(LOG_TAG, "createWindowSurface returned
> EGL_BAD_NATIVE_WINDOW.");
> return;
> }
> throw new RuntimeException("createWindowSurface failed "
> + GLUtils.getEGLErrorString(error));
> }
>
> if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface,
> mEglSurface, mEglContext)) {
> throw new RuntimeException("eglMakeCurrent failed "
> + GLUtils.getEGLErrorString(mEgl.eglGetError()));
> }
>
> mGL = mEglContext.getGL();
> }
>
>
> EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay,
> EGLConfig eglConfig) {
> int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2,
> EGL10.EGL_NONE };
> return egl.eglCreateContext(eglDisplay, eglConfig,
> EGL10.EGL_NO_CONTEXT, attrib_list);
> }
>
> private EGLConfig chooseEglConfig() {
> int[] configsCount = new int[1];
> EGLConfig[] configs = new EGLConfig[1];
> int[] configSpec = getConfig();
> if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1,
> configsCount)) {
> throw new IllegalArgumentException("eglChooseConfig failed
> " +
> GLUtils.getEGLErrorString(mEgl.eglGetError()));
> } else if (configsCount[0] > 0) {
> return configs[0];
> }
> return null;
> }
>
> private int[] getConfig() {
> return new int[] {
> EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
> EGL10.EGL_RED_SIZE, 8,
> EGL10.EGL_GREEN_SIZE, 8,
> EGL10.EGL_BLUE_SIZE, 8,
> EGL10.EGL_ALPHA_SIZE, 8,
> EGL10.EGL_DEPTH_SIZE, 0,
> EGL10.EGL_STENCIL_SIZE, 0,
> EGL10.EGL_NONE
> };
> }
>
> void finish() {
> mFinished = true;
> }
> }
>
> On Wed, Nov 23, 2011 at 8:54 AM, plafayette <[email protected]>wrote:
>
>> The TextureView documentation states that it can be used to render
>> OpenGL content.
>>
>> In the blog post announcing TextureView, it states:
>>
>> A TextureView can just as easily be used to embed an OpenGL scene in
>> your application. As of Android 4.0, eglCreateWindowSurface() can be
>> used to render into a SurfaceTexture object.
>>
>> Which seems to imply that to use TextureView instead of GLSurfaceView,
>> one would have to do all the EGL setup themselves and manage the
>> EGLContext and the threading (since GLSurfaceView maintains a
>> GLThread). There doesn't seem to be any sample code in the Android 4.0
>> SDK that demonstrates how the "TextureView can just as easily be used
>> to embed an OpenGL scene". TextureView seems to plug in more cleanly
>> to the Camera preview (setPreviewTexture) and MediaPlayer
>> (setSurface).
>>
>> Is it possible to use GLSurfaceView in conjunction with TextureView by
>> using GLSurfaceView.setEGLWindowSurfaceFactory to make it render to
>> the TextureView's SurfaceTexture?
>>
>> Again, it would be nice if there were some sample code.
>>
>> p.s. Posted on stackoverflow.com as well. Feel free to answer:
>>
>> http://stackoverflow.com/questions/8231978/how-to-replace-glsurfaceview-with-textureview-in-android-ice-cream-sandwich
>>
>> --
>> You received this message because you are subscribed to the Google
>> Groups "Android Developers" group.
>> To post to this group, send email to [email protected]
>> To unsubscribe from this group, send email to
>> [email protected]
>> For more options, visit this group at
>> http://groups.google.com/group/android-developers?hl=en
>>
>
>
>
> --
> Romain Guy
> Android framework engineer
> [email protected]
>
>
--
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en