/* * 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. */ #ifndef ANDROID_SURFACE_TEXTURE_GL_THREAD_TO_GL_H #define ANDROID_SURFACE_TEXTURE_GL_THREAD_TO_GL_H #include "SurfaceTextureGLToGL.h" namespace android { /* * This test fixture is for testing GL -> GL texture streaming from one thread * to another. It contains functionality to create a producer thread that will * perform GL rendering to an ANativeWindow that feeds frames to a * GLConsumer. Additionally it supports interlocking the producer and * consumer threads so that a specific sequence of calls can be * deterministically created by the test. * * The intended usage is as follows: * * TEST_F(...) { * class PT : public ProducerThread { * virtual void render() { * ... * swapBuffers(); * } * }; * * runProducerThread(new PT()); * * // The order of these calls will vary from test to test and may include * // multiple frames and additional operations (e.g. GL rendering from the * // texture). * fc->waitForFrame(); * mST->updateTexImage(); * fc->finishFrame(); * } * */ class SurfaceTextureGLThreadToGLTest : public SurfaceTextureGLToGLTest { protected: // ProducerThread is an abstract base class to simplify the creation of // OpenGL ES frame producer threads. class ProducerThread : public Thread { public: virtual ~ProducerThread() { } void setEglObjects(EGLDisplay producerEglDisplay, EGLSurface producerEglSurface, EGLContext producerEglContext) { mProducerEglDisplay = producerEglDisplay; mProducerEglSurface = producerEglSurface; mProducerEglContext = producerEglContext; } virtual bool threadLoop() { eglMakeCurrent(mProducerEglDisplay, mProducerEglSurface, mProducerEglSurface, mProducerEglContext); render(); eglMakeCurrent(mProducerEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); return false; } protected: virtual void render() = 0; void swapBuffers() { eglSwapBuffers(mProducerEglDisplay, mProducerEglSurface); } EGLDisplay mProducerEglDisplay; EGLSurface mProducerEglSurface; EGLContext mProducerEglContext; }; // FrameCondition is a utility class for interlocking between the producer // and consumer threads. The FrameCondition object should be created and // destroyed in the consumer thread only. The consumer thread should set // the FrameCondition as the FrameAvailableListener of the GLConsumer, // and should call both waitForFrame and finishFrame once for each expected // frame. // // This interlocking relies on the fact that onFrameAvailable gets called // synchronously from GLConsumer::queueBuffer. class FrameCondition : public GLConsumer::FrameAvailableListener { public: FrameCondition(): mFrameAvailable(false), mFrameFinished(false) { } // waitForFrame waits for the next frame to arrive. This should be // called from the consumer thread once for every frame expected by the // test. void waitForFrame() { Mutex::Autolock lock(mMutex); ALOGV("+waitForFrame"); while (!mFrameAvailable) { mFrameAvailableCondition.wait(mMutex); } mFrameAvailable = false; ALOGV("-waitForFrame"); } // Allow the producer to return from its swapBuffers call and continue // on to produce the next frame. This should be called by the consumer // thread once for every frame expected by the test. void finishFrame() { Mutex::Autolock lock(mMutex); ALOGV("+finishFrame"); mFrameFinished = true; mFrameFinishCondition.signal(); ALOGV("-finishFrame"); } // This should be called by GLConsumer on the producer thread. virtual void onFrameAvailable(const BufferItem& /* item */) { Mutex::Autolock lock(mMutex); ALOGV("+onFrameAvailable"); mFrameAvailable = true; mFrameAvailableCondition.signal(); while (!mFrameFinished) { mFrameFinishCondition.wait(mMutex); } mFrameFinished = false; ALOGV("-onFrameAvailable"); } protected: bool mFrameAvailable; bool mFrameFinished; Mutex mMutex; Condition mFrameAvailableCondition; Condition mFrameFinishCondition; }; virtual void SetUp() { SurfaceTextureGLToGLTest::SetUp(); mFC = new FrameCondition(); mST->setFrameAvailableListener(mFC); } virtual void TearDown() { if (mProducerThread != NULL) { mProducerThread->requestExitAndWait(); } mProducerThread.clear(); mFC.clear(); SurfaceTextureGLToGLTest::TearDown(); } void runProducerThread(const sp producerThread) { ASSERT_TRUE(mProducerThread == NULL); mProducerThread = producerThread; producerThread->setEglObjects(mEglDisplay, mProducerEglSurface, mProducerEglContext); producerThread->run(); } sp mProducerThread; sp mFC; }; } // namespace android #endif