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 <pierre.lafaye...@gmail.com>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 android-developers@googlegroups.com >> To unsubscribe from this group, send email to >> android-developers+unsubscr...@googlegroups.com >> For more options, visit this group at >> http://groups.google.com/group/android-developers?hl=en >> > > > > -- > Romain Guy > Android framework engineer > romain...@android.com > > -- You received this message because you are subscribed to the Google Groups "Android Developers" group. To post to this group, send email to android-developers@googlegroups.com To unsubscribe from this group, send email to android-developers+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/android-developers?hl=en