|  | /* | 
|  | * Copyright 2013 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | #define LOG_TAG "SurfaceTextureMultiContextGL_test" | 
|  | //#define LOG_NDEBUG 0 | 
|  |  | 
|  | #include "SurfaceTextureMultiContextGL.h" | 
|  |  | 
|  | #include "FillBuffer.h" | 
|  |  | 
|  | #include <GLES/glext.h> | 
|  |  | 
|  | namespace android { | 
|  |  | 
|  | TEST_F(SurfaceTextureMultiContextGLTest, UpdateFromMultipleContextsFails) { | 
|  | ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); | 
|  |  | 
|  | // Latch the texture contents on the primary context. | 
|  | mFW->waitForFrame(); | 
|  | ASSERT_EQ(OK, mST->updateTexImage()); | 
|  |  | 
|  | // Attempt to latch the texture on the secondary context. | 
|  | ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, | 
|  | mSecondEglContext)); | 
|  | ASSERT_EQ(EGL_SUCCESS, eglGetError()); | 
|  | ASSERT_EQ(INVALID_OPERATION, mST->updateTexImage()); | 
|  | } | 
|  |  | 
|  | TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextSucceeds) { | 
|  | ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); | 
|  |  | 
|  | // Latch the texture contents on the primary context. | 
|  | mFW->waitForFrame(); | 
|  | ASSERT_EQ(OK, mST->updateTexImage()); | 
|  |  | 
|  | // Detach from the primary context. | 
|  | ASSERT_EQ(OK, mST->detachFromContext()); | 
|  |  | 
|  | // Check that the GL texture was deleted. | 
|  | EXPECT_EQ(GL_FALSE, glIsTexture(TEX_ID)); | 
|  | } | 
|  |  | 
|  | TEST_F(SurfaceTextureMultiContextGLTest, | 
|  | DetachFromContextSucceedsAfterProducerDisconnect) { | 
|  | ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); | 
|  |  | 
|  | // Latch the texture contents on the primary context. | 
|  | mFW->waitForFrame(); | 
|  | ASSERT_EQ(OK, mST->updateTexImage()); | 
|  |  | 
|  | // Detach from the primary context. | 
|  | native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU); | 
|  | ASSERT_EQ(OK, mST->detachFromContext()); | 
|  |  | 
|  | // Check that the GL texture was deleted. | 
|  | EXPECT_EQ(GL_FALSE, glIsTexture(TEX_ID)); | 
|  | } | 
|  |  | 
|  | TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextFailsWhenAbandoned) { | 
|  | ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); | 
|  |  | 
|  | // Latch the texture contents on the primary context. | 
|  | mFW->waitForFrame(); | 
|  | ASSERT_EQ(OK, mST->updateTexImage()); | 
|  |  | 
|  | // Attempt to detach from the primary context. | 
|  | mST->abandon(); | 
|  | ASSERT_EQ(NO_INIT, mST->detachFromContext()); | 
|  | } | 
|  |  | 
|  | TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextFailsWhenDetached) { | 
|  | ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); | 
|  |  | 
|  | // Latch the texture contents on the primary context. | 
|  | mFW->waitForFrame(); | 
|  | ASSERT_EQ(OK, mST->updateTexImage()); | 
|  |  | 
|  | // Detach from the primary context. | 
|  | ASSERT_EQ(OK, mST->detachFromContext()); | 
|  |  | 
|  | // Attempt to detach from the primary context again. | 
|  | ASSERT_EQ(INVALID_OPERATION, mST->detachFromContext()); | 
|  | } | 
|  |  | 
|  | TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextFailsWithNoDisplay) { | 
|  | ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); | 
|  |  | 
|  | // Latch the texture contents on the primary context. | 
|  | mFW->waitForFrame(); | 
|  | ASSERT_EQ(OK, mST->updateTexImage()); | 
|  |  | 
|  | // Make there be no current display. | 
|  | ASSERT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, | 
|  | EGL_NO_CONTEXT)); | 
|  | ASSERT_EQ(EGL_SUCCESS, eglGetError()); | 
|  |  | 
|  | // Attempt to detach from the primary context. | 
|  | ASSERT_EQ(INVALID_OPERATION, mST->detachFromContext()); | 
|  | } | 
|  |  | 
|  | TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextFailsWithNoContext) { | 
|  | ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); | 
|  |  | 
|  | // Latch the texture contents on the primary context. | 
|  | mFW->waitForFrame(); | 
|  | ASSERT_EQ(OK, mST->updateTexImage()); | 
|  |  | 
|  | // Make current context be incorrect. | 
|  | ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, | 
|  | mSecondEglContext)); | 
|  | ASSERT_EQ(EGL_SUCCESS, eglGetError()); | 
|  |  | 
|  | // Attempt to detach from the primary context. | 
|  | ASSERT_EQ(INVALID_OPERATION, mST->detachFromContext()); | 
|  | } | 
|  |  | 
|  | TEST_F(SurfaceTextureMultiContextGLTest, UpdateTexImageFailsWhenDetached) { | 
|  | ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); | 
|  |  | 
|  | // Detach from the primary context. | 
|  | ASSERT_EQ(OK, mST->detachFromContext()); | 
|  |  | 
|  | // Attempt to latch the texture contents on the primary context. | 
|  | mFW->waitForFrame(); | 
|  | ASSERT_EQ(INVALID_OPERATION, mST->updateTexImage()); | 
|  | } | 
|  |  | 
|  | TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextSucceeds) { | 
|  | ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); | 
|  |  | 
|  | // Latch the texture contents on the primary context. | 
|  | mFW->waitForFrame(); | 
|  | ASSERT_EQ(OK, mST->updateTexImage()); | 
|  |  | 
|  | // Detach from the primary context. | 
|  | ASSERT_EQ(OK, mST->detachFromContext()); | 
|  |  | 
|  | // Attach to the secondary context. | 
|  | ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, | 
|  | mSecondEglContext)); | 
|  | ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); | 
|  |  | 
|  | // Verify that the texture object was created and bound. | 
|  | GLint texBinding = -1; | 
|  | glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texBinding); | 
|  | EXPECT_EQ(SECOND_TEX_ID, texBinding); | 
|  |  | 
|  | // Try to use the texture from the secondary context. | 
|  | glClearColor(0.2, 0.2, 0.2, 0.2); | 
|  | glClear(GL_COLOR_BUFFER_BIT); | 
|  | glViewport(0, 0, 1, 1); | 
|  | mSecondTextureRenderer->drawTexture(); | 
|  | ASSERT_TRUE(checkPixel( 0,  0,  35,  35,  35,  35)); | 
|  | ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); | 
|  | } | 
|  |  | 
|  | TEST_F(SurfaceTextureMultiContextGLTest, | 
|  | AttachToContextSucceedsAfterProducerDisconnect) { | 
|  | ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); | 
|  |  | 
|  | // Latch the texture contents on the primary context. | 
|  | mFW->waitForFrame(); | 
|  | ASSERT_EQ(OK, mST->updateTexImage()); | 
|  |  | 
|  | // Detach from the primary context. | 
|  | native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU); | 
|  | ASSERT_EQ(OK, mST->detachFromContext()); | 
|  |  | 
|  | // Attach to the secondary context. | 
|  | ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, | 
|  | mSecondEglContext)); | 
|  | ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); | 
|  |  | 
|  | // Verify that the texture object was created and bound. | 
|  | GLint texBinding = -1; | 
|  | glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texBinding); | 
|  | EXPECT_EQ(SECOND_TEX_ID, texBinding); | 
|  |  | 
|  | // Try to use the texture from the secondary context. | 
|  | glClearColor(0.2, 0.2, 0.2, 0.2); | 
|  | glClear(GL_COLOR_BUFFER_BIT); | 
|  | glViewport(0, 0, 1, 1); | 
|  | mSecondTextureRenderer->drawTexture(); | 
|  | ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); | 
|  | ASSERT_TRUE(checkPixel( 0,  0,  35,  35,  35,  35)); | 
|  | } | 
|  |  | 
|  | TEST_F(SurfaceTextureMultiContextGLTest, | 
|  | AttachToContextSucceedsBeforeUpdateTexImage) { | 
|  | ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); | 
|  |  | 
|  | // Detach from the primary context. | 
|  | native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU); | 
|  | ASSERT_EQ(OK, mST->detachFromContext()); | 
|  |  | 
|  | // Attach to the secondary context. | 
|  | ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, | 
|  | mSecondEglContext)); | 
|  | ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); | 
|  |  | 
|  | // Verify that the texture object was created and bound. | 
|  | GLint texBinding = -1; | 
|  | glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texBinding); | 
|  | EXPECT_EQ(SECOND_TEX_ID, texBinding); | 
|  |  | 
|  | // Latch the texture contents on the primary context. | 
|  | mFW->waitForFrame(); | 
|  | ASSERT_EQ(OK, mST->updateTexImage()); | 
|  |  | 
|  | // Try to use the texture from the secondary context. | 
|  | glClearColor(0.2, 0.2, 0.2, 0.2); | 
|  | glClear(GL_COLOR_BUFFER_BIT); | 
|  | glViewport(0, 0, 1, 1); | 
|  | mSecondTextureRenderer->drawTexture(); | 
|  | ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); | 
|  | ASSERT_TRUE(checkPixel( 0,  0,  35,  35,  35,  35)); | 
|  | } | 
|  |  | 
|  | TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextFailsWhenAbandoned) { | 
|  | ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); | 
|  |  | 
|  | // Latch the texture contents on the primary context. | 
|  | mFW->waitForFrame(); | 
|  | ASSERT_EQ(OK, mST->updateTexImage()); | 
|  |  | 
|  | // Detach from the primary context. | 
|  | ASSERT_EQ(OK, mST->detachFromContext()); | 
|  |  | 
|  | // Attempt to attach to the secondary context. | 
|  | mST->abandon(); | 
|  |  | 
|  | // Attempt to attach to the primary context. | 
|  | ASSERT_EQ(NO_INIT, mST->attachToContext(SECOND_TEX_ID)); | 
|  | } | 
|  |  | 
|  | TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextFailsWhenAttached) { | 
|  | ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); | 
|  |  | 
|  | // Latch the texture contents on the primary context. | 
|  | mFW->waitForFrame(); | 
|  | ASSERT_EQ(OK, mST->updateTexImage()); | 
|  |  | 
|  | // Attempt to attach to the primary context. | 
|  | ASSERT_EQ(INVALID_OPERATION, mST->attachToContext(SECOND_TEX_ID)); | 
|  | } | 
|  |  | 
|  | TEST_F(SurfaceTextureMultiContextGLTest, | 
|  | AttachToContextFailsWhenAttachedBeforeUpdateTexImage) { | 
|  | ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); | 
|  |  | 
|  | // Attempt to attach to the primary context. | 
|  | ASSERT_EQ(INVALID_OPERATION, mST->attachToContext(SECOND_TEX_ID)); | 
|  | } | 
|  |  | 
|  | TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextFailsWithNoDisplay) { | 
|  | ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); | 
|  |  | 
|  | // Latch the texture contents on the primary context. | 
|  | mFW->waitForFrame(); | 
|  | ASSERT_EQ(OK, mST->updateTexImage()); | 
|  |  | 
|  | // Detach from the primary context. | 
|  | ASSERT_EQ(OK, mST->detachFromContext()); | 
|  |  | 
|  | // Make there be no current display. | 
|  | ASSERT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, | 
|  | EGL_NO_CONTEXT)); | 
|  | ASSERT_EQ(EGL_SUCCESS, eglGetError()); | 
|  |  | 
|  | // Attempt to attach with no context current. | 
|  | ASSERT_EQ(INVALID_OPERATION, mST->attachToContext(SECOND_TEX_ID)); | 
|  | } | 
|  |  | 
|  | TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextSucceedsTwice) { | 
|  | ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); | 
|  |  | 
|  | // Latch the texture contents on the primary context. | 
|  | mFW->waitForFrame(); | 
|  | ASSERT_EQ(OK, mST->updateTexImage()); | 
|  |  | 
|  | // Detach from the primary context. | 
|  | ASSERT_EQ(OK, mST->detachFromContext()); | 
|  |  | 
|  | // Attach to the secondary context. | 
|  | ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, | 
|  | mSecondEglContext)); | 
|  | ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); | 
|  |  | 
|  | // Detach from the secondary context. | 
|  | ASSERT_EQ(OK, mST->detachFromContext()); | 
|  |  | 
|  | // Attach to the tertiary context. | 
|  | ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, | 
|  | mThirdEglContext)); | 
|  | ASSERT_EQ(OK, mST->attachToContext(THIRD_TEX_ID)); | 
|  |  | 
|  | // Verify that the texture object was created and bound. | 
|  | GLint texBinding = -1; | 
|  | glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texBinding); | 
|  | EXPECT_EQ(THIRD_TEX_ID, texBinding); | 
|  |  | 
|  | // Try to use the texture from the tertiary context. | 
|  | glClearColor(0.2, 0.2, 0.2, 0.2); | 
|  | glClear(GL_COLOR_BUFFER_BIT); | 
|  | glViewport(0, 0, 1, 1); | 
|  | mThirdTextureRenderer->drawTexture(); | 
|  | ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); | 
|  | ASSERT_TRUE(checkPixel( 0,  0,  35,  35,  35,  35)); | 
|  | } | 
|  |  | 
|  | TEST_F(SurfaceTextureMultiContextGLTest, | 
|  | AttachToContextSucceedsTwiceBeforeUpdateTexImage) { | 
|  | ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); | 
|  |  | 
|  | // Detach from the primary context. | 
|  | ASSERT_EQ(OK, mST->detachFromContext()); | 
|  |  | 
|  | // Attach to the secondary context. | 
|  | ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, | 
|  | mSecondEglContext)); | 
|  | ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); | 
|  |  | 
|  | // Detach from the secondary context. | 
|  | ASSERT_EQ(OK, mST->detachFromContext()); | 
|  |  | 
|  | // Attach to the tertiary context. | 
|  | ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, | 
|  | mThirdEglContext)); | 
|  | ASSERT_EQ(OK, mST->attachToContext(THIRD_TEX_ID)); | 
|  |  | 
|  | // Verify that the texture object was created and bound. | 
|  | GLint texBinding = -1; | 
|  | glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texBinding); | 
|  | EXPECT_EQ(THIRD_TEX_ID, texBinding); | 
|  |  | 
|  | // Latch the texture contents on the tertiary context. | 
|  | mFW->waitForFrame(); | 
|  | ASSERT_EQ(OK, mST->updateTexImage()); | 
|  |  | 
|  | // Try to use the texture from the tertiary context. | 
|  | glClearColor(0.2, 0.2, 0.2, 0.2); | 
|  | glClear(GL_COLOR_BUFFER_BIT); | 
|  | glViewport(0, 0, 1, 1); | 
|  | mThirdTextureRenderer->drawTexture(); | 
|  | ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); | 
|  | ASSERT_TRUE(checkPixel( 0,  0,  35,  35,  35,  35)); | 
|  | } | 
|  |  | 
|  | TEST_F(SurfaceTextureMultiContextGLTest, | 
|  | UpdateTexImageSucceedsForBufferConsumedBeforeDetach) { | 
|  | ASSERT_EQ(NO_ERROR, mST->setDefaultMaxBufferCount(2)); | 
|  |  | 
|  | // produce two frames and consume them both on the primary context | 
|  | ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); | 
|  | mFW->waitForFrame(); | 
|  | ASSERT_EQ(OK, mST->updateTexImage()); | 
|  |  | 
|  | ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); | 
|  | mFW->waitForFrame(); | 
|  | ASSERT_EQ(OK, mST->updateTexImage()); | 
|  |  | 
|  | // produce one more frame | 
|  | ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); | 
|  |  | 
|  | // Detach from the primary context and attach to the secondary context | 
|  | ASSERT_EQ(OK, mST->detachFromContext()); | 
|  | ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, | 
|  | mSecondEglContext)); | 
|  | ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); | 
|  |  | 
|  | // Consume final frame on secondary context | 
|  | mFW->waitForFrame(); | 
|  | ASSERT_EQ(OK, mST->updateTexImage()); | 
|  | } | 
|  |  | 
|  | TEST_F(SurfaceTextureMultiContextGLTest, | 
|  | AttachAfterDisplayTerminatedSucceeds) { | 
|  | ASSERT_EQ(NO_ERROR, mST->setDefaultMaxBufferCount(2)); | 
|  |  | 
|  | // produce two frames and consume them both on the primary context | 
|  | ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); | 
|  | mFW->waitForFrame(); | 
|  | ASSERT_EQ(OK, mST->updateTexImage()); | 
|  |  | 
|  | ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); | 
|  | mFW->waitForFrame(); | 
|  | ASSERT_EQ(OK, mST->updateTexImage()); | 
|  |  | 
|  | // produce one more frame | 
|  | ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); | 
|  |  | 
|  | // Detach from the primary context. | 
|  | ASSERT_EQ(OK, mST->releaseTexImage()); | 
|  | ASSERT_EQ(OK, mST->detachFromContext()); | 
|  |  | 
|  | // Terminate and then initialize the display. All contexts, surfaces | 
|  | // and images are invalid at this point. | 
|  | EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); | 
|  | ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay); | 
|  | EGLint majorVersion = 0; | 
|  | EGLint minorVersion = 0; | 
|  | EXPECT_TRUE(eglTerminate(display)); | 
|  | EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion)); | 
|  | ASSERT_EQ(EGL_SUCCESS, eglGetError()); | 
|  |  | 
|  | // The surface is invalid so create it again. | 
|  | EGLint pbufferAttribs[] = { | 
|  | EGL_WIDTH, 64, | 
|  | EGL_HEIGHT, 64, | 
|  | EGL_NONE }; | 
|  | mEglSurface = eglCreatePbufferSurface(mEglDisplay, mGlConfig, | 
|  | pbufferAttribs); | 
|  |  | 
|  | // The second context is invalid so create it again. | 
|  | mSecondEglContext = eglCreateContext(mEglDisplay, mGlConfig, | 
|  | EGL_NO_CONTEXT, getContextAttribs()); | 
|  | ASSERT_EQ(EGL_SUCCESS, eglGetError()); | 
|  | ASSERT_NE(EGL_NO_CONTEXT, mSecondEglContext); | 
|  |  | 
|  | ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, | 
|  | mSecondEglContext)); | 
|  | ASSERT_EQ(EGL_SUCCESS, eglGetError()); | 
|  |  | 
|  | // Now attach to and consume final frame on secondary context. | 
|  | ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); | 
|  | mFW->waitForFrame(); | 
|  | ASSERT_EQ(OK, mST->updateTexImage()); | 
|  | } | 
|  |  | 
|  |  | 
|  | } // namespace android |