/*
 * 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 "SurfaceTextureGLThreadToGL_test"
//#define LOG_NDEBUG 0

#include "SurfaceTextureGLThreadToGL.h"

namespace android {

TEST_F(SurfaceTextureGLThreadToGLTest,
        UpdateTexImageBeforeFrameFinishedCompletes) {
    class PT : public ProducerThread {
        virtual void render() {
            glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
            glClear(GL_COLOR_BUFFER_BIT);
            swapBuffers();
        }
    };

    runProducerThread(new PT());

    mFC->waitForFrame();
    ASSERT_EQ(NO_ERROR, mST->updateTexImage());
    mFC->finishFrame();

    // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
}

TEST_F(SurfaceTextureGLThreadToGLTest,
        UpdateTexImageAfterFrameFinishedCompletes) {
    class PT : public ProducerThread {
        virtual void render() {
            glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
            glClear(GL_COLOR_BUFFER_BIT);
            swapBuffers();
        }
    };

    runProducerThread(new PT());

    mFC->waitForFrame();
    mFC->finishFrame();
    ASSERT_EQ(NO_ERROR, mST->updateTexImage());

    // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
}

TEST_F(SurfaceTextureGLThreadToGLTest,
        RepeatedUpdateTexImageBeforeFrameFinishedCompletes) {
    enum { NUM_ITERATIONS = 1024 };

    class PT : public ProducerThread {
        virtual void render() {
            for (int i = 0; i < NUM_ITERATIONS; i++) {
                glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
                glClear(GL_COLOR_BUFFER_BIT);
                ALOGV("+swapBuffers");
                swapBuffers();
                ALOGV("-swapBuffers");
            }
        }
    };

    runProducerThread(new PT());

    for (int i = 0; i < NUM_ITERATIONS; i++) {
        mFC->waitForFrame();
        ALOGV("+updateTexImage");
        ASSERT_EQ(NO_ERROR, mST->updateTexImage());
        ALOGV("-updateTexImage");
        mFC->finishFrame();

        // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
    }
}

TEST_F(SurfaceTextureGLThreadToGLTest,
        RepeatedUpdateTexImageAfterFrameFinishedCompletes) {
    enum { NUM_ITERATIONS = 1024 };

    class PT : public ProducerThread {
        virtual void render() {
            for (int i = 0; i < NUM_ITERATIONS; i++) {
                glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
                glClear(GL_COLOR_BUFFER_BIT);
                ALOGV("+swapBuffers");
                swapBuffers();
                ALOGV("-swapBuffers");
            }
        }
    };

    runProducerThread(new PT());

    for (int i = 0; i < NUM_ITERATIONS; i++) {
        mFC->waitForFrame();
        mFC->finishFrame();
        ALOGV("+updateTexImage");
        ASSERT_EQ(NO_ERROR, mST->updateTexImage());
        ALOGV("-updateTexImage");

        // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
    }
}

// XXX: This test is disabled because it is currently hanging on some devices.
TEST_F(SurfaceTextureGLThreadToGLTest,
        DISABLED_RepeatedSwapBuffersWhileDequeueStalledCompletes) {
    enum { NUM_ITERATIONS = 64 };

    class PT : public ProducerThread {
        virtual void render() {
            for (int i = 0; i < NUM_ITERATIONS; i++) {
                glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
                glClear(GL_COLOR_BUFFER_BIT);
                ALOGV("+swapBuffers");
                swapBuffers();
                ALOGV("-swapBuffers");
            }
        }
    };

    ASSERT_EQ(OK, mST->setDefaultMaxBufferCount(2));

    runProducerThread(new PT());

    // Allow three frames to be rendered and queued before starting the
    // rendering in this thread.  For the latter two frames we don't call
    // updateTexImage so the next dequeue from the producer thread will block
    // waiting for a frame to become available.
    mFC->waitForFrame();
    mFC->finishFrame();

    // We must call updateTexImage to consume the first frame so that the
    // SurfaceTexture is able to reduce the buffer count to 2.  This is because
    // the GL driver may dequeue a buffer when the EGLSurface is created, and
    // that happens before we call setDefaultMaxBufferCount.  It's possible that the
    // driver does not dequeue a buffer at EGLSurface creation time, so we
    // cannot rely on this to cause the second dequeueBuffer call to block.
    ASSERT_EQ(NO_ERROR, mST->updateTexImage());

    mFC->waitForFrame();
    mFC->finishFrame();
    mFC->waitForFrame();
    mFC->finishFrame();

    // Sleep for 100ms to allow the producer thread's dequeueBuffer call to
    // block waiting for a buffer to become available.
    usleep(100000);

    // Render and present a number of images.  This thread should not be blocked
    // by the fact that the producer thread is blocking in dequeue.
    for (int i = 0; i < NUM_ITERATIONS; i++) {
        glClear(GL_COLOR_BUFFER_BIT);
        eglSwapBuffers(mEglDisplay, mEglSurface);
    }

    // Consume the two pending buffers to unblock the producer thread.
    ASSERT_EQ(NO_ERROR, mST->updateTexImage());
    ASSERT_EQ(NO_ERROR, mST->updateTexImage());

    // Consume the remaining buffers from the producer thread.
    for (int i = 0; i < NUM_ITERATIONS-3; i++) {
        mFC->waitForFrame();
        mFC->finishFrame();
        ALOGV("+updateTexImage");
        ASSERT_EQ(NO_ERROR, mST->updateTexImage());
        ALOGV("-updateTexImage");
    }
}

} // namespace android
