1/*
2 * Copyright 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef ANDROID_SURFACE_TEXTURE_GL_THREAD_TO_GL_H
18#define ANDROID_SURFACE_TEXTURE_GL_THREAD_TO_GL_H
19
20#include "SurfaceTextureGLToGL.h"
21
22namespace android {
23
24/*
25 * This test fixture is for testing GL -> GL texture streaming from one thread
26 * to another.  It contains functionality to create a producer thread that will
27 * perform GL rendering to an ANativeWindow that feeds frames to a
28 * GLConsumer.  Additionally it supports interlocking the producer and
29 * consumer threads so that a specific sequence of calls can be
30 * deterministically created by the test.
31 *
32 * The intended usage is as follows:
33 *
34 * TEST_F(...) {
35 *     class PT : public ProducerThread {
36 *         virtual void render() {
37 *             ...
38 *             swapBuffers();
39 *         }
40 *     };
41 *
42 *     runProducerThread(new PT());
43 *
44 *     // The order of these calls will vary from test to test and may include
45 *     // multiple frames and additional operations (e.g. GL rendering from the
46 *     // texture).
47 *     fc->waitForFrame();
48 *     mST->updateTexImage();
49 *     fc->finishFrame();
50 * }
51 *
52 */
53class SurfaceTextureGLThreadToGLTest : public SurfaceTextureGLToGLTest {
54protected:
55
56    // ProducerThread is an abstract base class to simplify the creation of
57    // OpenGL ES frame producer threads.
58    class ProducerThread : public Thread {
59    public:
60        virtual ~ProducerThread() {
61        }
62
63        void setEglObjects(EGLDisplay producerEglDisplay,
64                EGLSurface producerEglSurface,
65                EGLContext producerEglContext) {
66            mProducerEglDisplay = producerEglDisplay;
67            mProducerEglSurface = producerEglSurface;
68            mProducerEglContext = producerEglContext;
69        }
70
71        virtual bool threadLoop() {
72            eglMakeCurrent(mProducerEglDisplay, mProducerEglSurface,
73                    mProducerEglSurface, mProducerEglContext);
74            render();
75            eglMakeCurrent(mProducerEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
76                    EGL_NO_CONTEXT);
77            return false;
78        }
79
80    protected:
81        virtual void render() = 0;
82
83        void swapBuffers() {
84            eglSwapBuffers(mProducerEglDisplay, mProducerEglSurface);
85        }
86
87        EGLDisplay mProducerEglDisplay;
88        EGLSurface mProducerEglSurface;
89        EGLContext mProducerEglContext;
90    };
91
92    // FrameCondition is a utility class for interlocking between the producer
93    // and consumer threads.  The FrameCondition object should be created and
94    // destroyed in the consumer thread only.  The consumer thread should set
95    // the FrameCondition as the FrameAvailableListener of the GLConsumer,
96    // and should call both waitForFrame and finishFrame once for each expected
97    // frame.
98    //
99    // This interlocking relies on the fact that onFrameAvailable gets called
100    // synchronously from GLConsumer::queueBuffer.
101    class FrameCondition : public GLConsumer::FrameAvailableListener {
102    public:
103        FrameCondition():
104                mFrameAvailable(false),
105                mFrameFinished(false) {
106        }
107
108        // waitForFrame waits for the next frame to arrive.  This should be
109        // called from the consumer thread once for every frame expected by the
110        // test.
111        void waitForFrame() {
112            Mutex::Autolock lock(mMutex);
113            ALOGV("+waitForFrame");
114            while (!mFrameAvailable) {
115                mFrameAvailableCondition.wait(mMutex);
116            }
117            mFrameAvailable = false;
118            ALOGV("-waitForFrame");
119        }
120
121        // Allow the producer to return from its swapBuffers call and continue
122        // on to produce the next frame.  This should be called by the consumer
123        // thread once for every frame expected by the test.
124        void finishFrame() {
125            Mutex::Autolock lock(mMutex);
126            ALOGV("+finishFrame");
127            mFrameFinished = true;
128            mFrameFinishCondition.signal();
129            ALOGV("-finishFrame");
130        }
131
132        // This should be called by GLConsumer on the producer thread.
133        virtual void onFrameAvailable(const BufferItem& /* item */) {
134            Mutex::Autolock lock(mMutex);
135            ALOGV("+onFrameAvailable");
136            mFrameAvailable = true;
137            mFrameAvailableCondition.signal();
138            while (!mFrameFinished) {
139                mFrameFinishCondition.wait(mMutex);
140            }
141            mFrameFinished = false;
142            ALOGV("-onFrameAvailable");
143        }
144
145    protected:
146        bool mFrameAvailable;
147        bool mFrameFinished;
148
149        Mutex mMutex;
150        Condition mFrameAvailableCondition;
151        Condition mFrameFinishCondition;
152    };
153
154    virtual void SetUp() {
155        SurfaceTextureGLToGLTest::SetUp();
156        mFC = new FrameCondition();
157        mST->setFrameAvailableListener(mFC);
158    }
159
160    virtual void TearDown() {
161        if (mProducerThread != NULL) {
162            mProducerThread->requestExitAndWait();
163        }
164        mProducerThread.clear();
165        mFC.clear();
166        SurfaceTextureGLToGLTest::TearDown();
167    }
168
169    void runProducerThread(const sp<ProducerThread> producerThread) {
170        ASSERT_TRUE(mProducerThread == NULL);
171        mProducerThread = producerThread;
172        producerThread->setEglObjects(mEglDisplay, mProducerEglSurface,
173                mProducerEglContext);
174        producerThread->run("ProducerThread");
175    }
176
177    sp<ProducerThread> mProducerThread;
178    sp<FrameCondition> mFC;
179};
180
181} // namespace android
182
183#endif
184