summaryrefslogtreecommitdiffstats
path: root/libs/gui/tests/SurfaceTextureGLThreadToGL.h
blob: 6410516d27ca7271dab99513ffc5bea27a82db02 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
/*
 * 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() {
            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> producerThread) {
        ASSERT_TRUE(mProducerThread == NULL);
        mProducerThread = producerThread;
        producerThread->setEglObjects(mEglDisplay, mProducerEglSurface,
                mProducerEglContext);
        producerThread->run();
    }

    sp<ProducerThread> mProducerThread;
    sp<FrameCondition> mFC;
};

} // namespace android

#endif