| /* | |
| * 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_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); | |
| 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_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); | |
| 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_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); | |
| 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_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); | |
| 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_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); | |
| 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_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); | |
| 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_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); | |
| 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_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); | |
| 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_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); | |
| 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_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); | |
| 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_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); | |
| 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_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); | |
| 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_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); | |
| 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_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); | |
| 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_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); | |
| 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_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); | |
| 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_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); | |
| 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(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); | |
| // 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(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); | |
| // 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 |