Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6f4cdfe0 authored by Jamie Gennis's avatar Jamie Gennis
Browse files

SurfaceTexture: clean up some tests

This change cleans a few things up in the SurfaceTexture tests:
- Wraps a few long lines.
- Refactors the multithreading portions of SurfaceTextureGLToGLTest into
  a new test fixture called SurfaceTextureGLThreadToGLTest.
- Changes some of the tests that were creating their own EGLSurface to
  use the SurfaceTextureGLToGLTest fixture.
- Reorders the test functions so that they are immediately below to the
  test fixture that they use.

Change-Id: I0491ce3528a7ff2b4f1e83602ba290269c087297
parent e1b2b00d
Loading
Loading
Loading
Loading
+255 −242
Original line number Original line Diff line number Diff line
@@ -396,7 +396,8 @@ protected:
            1.0f, 1.0f,
            1.0f, 1.0f,
        };
        };


        glVertexAttribPointer(mPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, triangleVertices);
        glVertexAttribPointer(mPositionHandle, 2, GL_FLOAT, GL_FALSE, 0,
                triangleVertices);
        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
        glEnableVertexAttribArray(mPositionHandle);
        glEnableVertexAttribArray(mPositionHandle);
        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
@@ -410,13 +411,17 @@ protected:
        // XXX: These calls are not needed for GL_TEXTURE_EXTERNAL_OES as
        // XXX: These calls are not needed for GL_TEXTURE_EXTERNAL_OES as
        // they're setting the defautls for that target, but when hacking things
        // they're setting the defautls for that target, but when hacking things
        // to use GL_TEXTURE_2D they are needed to achieve the same behavior.
        // to use GL_TEXTURE_2D they are needed to achieve the same behavior.
        glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER,
                GL_LINEAR);
        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
        glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER,
                GL_LINEAR);
        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
        glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S,
                GL_CLAMP_TO_EDGE);
        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
        glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T,
                GL_CLAMP_TO_EDGE);
        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());


        GLfloat texMatrix[16];
        GLfloat texMatrix[16];
@@ -640,8 +645,8 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) {


    for (int i = 0; i < 5; i++) {
    for (int i = 0; i < 5; i++) {
        const android_native_rect_t& crop(crops[i]);
        const android_native_rect_t& crop(crops[i]);
        SCOPED_TRACE(String8::format("rect{ l: %d t: %d r: %d b: %d }", crop.left,
        SCOPED_TRACE(String8::format("rect{ l: %d t: %d r: %d b: %d }",
                crop.top, crop.right, crop.bottom).string());
                crop.left, crop.top, crop.right, crop.bottom).string());


        ASSERT_EQ(NO_ERROR, native_window_set_crop(mANW.get(), &crop));
        ASSERT_EQ(NO_ERROR, native_window_set_crop(mANW.get(), &crop));


@@ -650,13 +655,15 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) {
        ASSERT_TRUE(anb != NULL);
        ASSERT_TRUE(anb != NULL);


        sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
        sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
        ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer()));
        ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(),
                buf->getNativeBuffer()));


        uint8_t* img = NULL;
        uint8_t* img = NULL;
        buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
        buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
        fillYV12BufferRect(img, texWidth, texHeight, buf->getStride(), crop);
        fillYV12BufferRect(img, texWidth, texHeight, buf->getStride(), crop);
        buf->unlock();
        buf->unlock();
        ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer()));
        ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(),
                buf->getNativeBuffer()));


        mST->updateTexImage();
        mST->updateTexImage();


@@ -708,7 +715,8 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) {


    class ProducerThread : public Thread {
    class ProducerThread : public Thread {
    public:
    public:
        ProducerThread(const sp<ANativeWindow>& anw, const TestPixel* testPixels):
        ProducerThread(const sp<ANativeWindow>& anw,
                const TestPixel* testPixels):
                mANW(anw),
                mANW(anw),
                mTestPixels(testPixels) {
                mTestPixels(testPixels) {
        }
        }
@@ -940,21 +948,173 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledRGBABufferPow2) {
    EXPECT_TRUE(checkPixel( 3, 52,  35, 231,  35,  35));
    EXPECT_TRUE(checkPixel( 3, 52,  35, 231,  35,  35));
}
}


TEST_F(SurfaceTextureGLTest, TexturingFromGLFilledRGBABufferPow2) {
TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) {
    class ProducerThread : public Thread {
    public:
        ProducerThread(const sp<ANativeWindow>& anw):
                mANW(anw),
                mDequeueError(NO_ERROR) {
        }

        virtual ~ProducerThread() {
        }

        virtual bool threadLoop() {
            Mutex::Autolock lock(mMutex);
            ANativeWindowBuffer* anb;

            // Frame 1
            if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) {
                return false;
            }
            if (anb == NULL) {
                return false;
            }
            if (mANW->queueBuffer(mANW.get(), anb)
                    != NO_ERROR) {
                return false;
            }

            // Frame 2
            if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) {
                return false;
            }
            if (anb == NULL) {
                return false;
            }
            if (mANW->queueBuffer(mANW.get(), anb)
                    != NO_ERROR) {
                return false;
            }

            // Frame 3 - error expected
            mDequeueError = mANW->dequeueBuffer(mANW.get(), &anb);
            return false;
        }

        status_t getDequeueError() {
            Mutex::Autolock lock(mMutex);
            return mDequeueError;
        }

    private:
        sp<ANativeWindow> mANW;
        status_t mDequeueError;
        Mutex mMutex;
    };

    sp<FrameWaiter> fw(new FrameWaiter);
    mST->setFrameAvailableListener(fw);
    ASSERT_EQ(OK, mST->setSynchronousMode(true));
    ASSERT_EQ(OK, mST->setBufferCountServer(2));

    sp<Thread> pt(new ProducerThread(mANW));
    pt->run();

    fw->waitForFrame();
    fw->waitForFrame();

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

    mST->abandon();

    pt->requestExitAndWait();
    ASSERT_EQ(NO_INIT,
            reinterpret_cast<ProducerThread*>(pt.get())->getDequeueError());
}

TEST_F(SurfaceTextureGLTest, InvalidWidthOrHeightFails) {
    int texHeight = 16;
    ANativeWindowBuffer* anb;

    GLint maxTextureSize;
    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);

    // make sure it works with small textures
    mST->setDefaultBufferSize(16, texHeight);
    EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
    EXPECT_EQ(16, anb->width);
    EXPECT_EQ(texHeight, anb->height);
    EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb));
    EXPECT_EQ(NO_ERROR, mST->updateTexImage());

    // make sure it works with GL_MAX_TEXTURE_SIZE
    mST->setDefaultBufferSize(maxTextureSize, texHeight);
    EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
    EXPECT_EQ(maxTextureSize, anb->width);
    EXPECT_EQ(texHeight, anb->height);
    EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb));
    EXPECT_EQ(NO_ERROR, mST->updateTexImage());

    // make sure it fails with GL_MAX_TEXTURE_SIZE+1
    mST->setDefaultBufferSize(maxTextureSize+1, texHeight);
    EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
    EXPECT_EQ(maxTextureSize+1, anb->width);
    EXPECT_EQ(texHeight, anb->height);
    EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb));
    ASSERT_NE(NO_ERROR, mST->updateTexImage());
}

/*
 * This test fixture is for testing GL -> GL texture streaming.  It creates an
 * EGLSurface and an EGLContext for the image producer to use.
 */
class SurfaceTextureGLToGLTest : public SurfaceTextureGLTest {
protected:
    SurfaceTextureGLToGLTest():
            mProducerEglSurface(EGL_NO_SURFACE),
            mProducerEglContext(EGL_NO_CONTEXT) {
    }

    virtual void SetUp() {
        SurfaceTextureGLTest::SetUp();

        EGLConfig myConfig = {0};
        EGLint numConfigs = 0;
        EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &myConfig,
                1, &numConfigs));
        ASSERT_EQ(EGL_SUCCESS, eglGetError());

        mProducerEglSurface = eglCreateWindowSurface(mEglDisplay, myConfig,
                mANW.get(), NULL);
        ASSERT_EQ(EGL_SUCCESS, eglGetError());
        ASSERT_NE(EGL_NO_SURFACE, mProducerEglSurface);

        mProducerEglContext = eglCreateContext(mEglDisplay, myConfig,
                EGL_NO_CONTEXT, getContextAttribs());
        ASSERT_EQ(EGL_SUCCESS, eglGetError());
        ASSERT_NE(EGL_NO_CONTEXT, mProducerEglContext);
    }

    virtual void TearDown() {
        if (mProducerEglContext != EGL_NO_CONTEXT) {
            eglDestroyContext(mEglDisplay, mProducerEglContext);
        }
        if (mProducerEglSurface != EGL_NO_SURFACE) {
            eglDestroySurface(mEglDisplay, mProducerEglSurface);
        }
        SurfaceTextureGLTest::TearDown();
    }

    EGLSurface mProducerEglSurface;
    EGLContext mProducerEglContext;
};

TEST_F(SurfaceTextureGLToGLTest, TexturingFromGLFilledRGBABufferPow2) {
    const int texWidth = 64;
    const int texWidth = 64;
    const int texHeight = 64;
    const int texHeight = 64;


    mST->setDefaultBufferSize(texWidth, texHeight);
    mST->setDefaultBufferSize(texWidth, texHeight);


    // Do the producer side of things
    // Do the producer side of things
    EGLSurface stcEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface,
            mANW.get(), NULL);
            mProducerEglSurface, mProducerEglContext));
    ASSERT_EQ(EGL_SUCCESS, eglGetError());
    ASSERT_EQ(EGL_SUCCESS, eglGetError());
    ASSERT_NE(EGL_NO_SURFACE, stcEglSurface);


    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, stcEglSurface, stcEglSurface,
    // This is needed to ensure we pick up a buffer of the correct size.
            mEglContext));
    eglSwapBuffers(mEglDisplay, mProducerEglSurface);
    ASSERT_EQ(EGL_SUCCESS, eglGetError());


    glClearColor(0.6, 0.6, 0.6, 0.6);
    glClearColor(0.6, 0.6, 0.6, 0.6);
    glClear(GL_COLOR_BUFFER_BIT);
    glClear(GL_COLOR_BUFFER_BIT);
@@ -972,7 +1132,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromGLFilledRGBABufferPow2) {
    glClearColor(0.0, 0.0, 1.0, 1.0);
    glClearColor(0.0, 0.0, 1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glClear(GL_COLOR_BUFFER_BIT);


    eglSwapBuffers(mEglDisplay, stcEglSurface);
    eglSwapBuffers(mEglDisplay, mProducerEglSurface);


    // Do the consumer side of things
    // Do the consumer side of things
    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
@@ -981,12 +1141,9 @@ TEST_F(SurfaceTextureGLTest, TexturingFromGLFilledRGBABufferPow2) {


    glDisable(GL_SCISSOR_TEST);
    glDisable(GL_SCISSOR_TEST);


    mST->updateTexImage(); // Skip the first frame, which was empty
    mST->updateTexImage();
    mST->updateTexImage();


    // We must wait until updateTexImage has been called to destroy the
    // EGLSurface because we're in synchronous mode.
    eglDestroySurface(mEglDisplay, stcEglSurface);

    glClearColor(0.2, 0.2, 0.2, 0.2);
    glClearColor(0.2, 0.2, 0.2, 0.2);
    glClear(GL_COLOR_BUFFER_BIT);
    glClear(GL_COLOR_BUFFER_BIT);


@@ -1016,90 +1173,85 @@ TEST_F(SurfaceTextureGLTest, TexturingFromGLFilledRGBABufferPow2) {
    EXPECT_TRUE(checkPixel( 3, 52, 153, 153, 153, 153));
    EXPECT_TRUE(checkPixel( 3, 52, 153, 153, 153, 153));
}
}


TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) {
TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceUnrefsBuffers) {
    class ProducerThread : public Thread {
    sp<GraphicBuffer> buffers[3];
    public:
        ProducerThread(const sp<ANativeWindow>& anw):
                mANW(anw),
                mDequeueError(NO_ERROR) {
        }

        virtual ~ProducerThread() {
        }


        virtual bool threadLoop() {
    for (int i = 0; i < 3; i++) {
            Mutex::Autolock lock(mMutex);
        // Produce a frame
            ANativeWindowBuffer* anb;
        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface,
                mProducerEglSurface, mProducerEglContext));
        ASSERT_EQ(EGL_SUCCESS, eglGetError());
        glClear(GL_COLOR_BUFFER_BIT);
        eglSwapBuffers(mEglDisplay, mProducerEglSurface);


            // Frame 1
        // Consume a frame
            if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) {
        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
                return false;
                mEglContext));
            }
        ASSERT_EQ(EGL_SUCCESS, eglGetError());
            if (anb == NULL) {
        mST->updateTexImage();
                return false;
        buffers[i] = mST->getCurrentBuffer();
            }
            if (mANW->queueBuffer(mANW.get(), anb)
                    != NO_ERROR) {
                return false;
    }
    }


            // Frame 2
    // Destroy the GL texture object to release its ref on buffers[2].
            if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) {
    GLuint texID = TEX_ID;
                return false;
    glDeleteTextures(1, &texID);
            }
            if (anb == NULL) {
                return false;
            }
            if (mANW->queueBuffer(mANW.get(), anb)
                    != NO_ERROR) {
                return false;
            }


            // Frame 3 - error expected
    // Destroy the EGLSurface
            mDequeueError = mANW->dequeueBuffer(mANW.get(), &anb);
    EXPECT_TRUE(eglDestroySurface(mEglDisplay, mProducerEglSurface));
            return false;
    ASSERT_EQ(EGL_SUCCESS, eglGetError());
        }


        status_t getDequeueError() {
    // Release the ref that the SurfaceTexture has on buffers[2].
            Mutex::Autolock lock(mMutex);
    mST->abandon();
            return mDequeueError;

    EXPECT_EQ(1, buffers[0]->getStrongCount());
    EXPECT_EQ(1, buffers[1]->getStrongCount());
    EXPECT_EQ(1, buffers[2]->getStrongCount());
}
}


    private:
TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceAfterAbandonUnrefsBuffers) {
        sp<ANativeWindow> mANW;
    sp<GraphicBuffer> buffers[3];
        status_t mDequeueError;
        Mutex mMutex;
    };


    sp<FrameWaiter> fw(new FrameWaiter);
    for (int i = 0; i < 3; i++) {
    mST->setFrameAvailableListener(fw);
        // Produce a frame
    ASSERT_EQ(OK, mST->setSynchronousMode(true));
        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface,
    ASSERT_EQ(OK, mST->setBufferCountServer(2));
                mProducerEglSurface, mProducerEglContext));
        ASSERT_EQ(EGL_SUCCESS, eglGetError());
        glClear(GL_COLOR_BUFFER_BIT);
        EXPECT_TRUE(eglSwapBuffers(mEglDisplay, mProducerEglSurface));
        ASSERT_EQ(EGL_SUCCESS, eglGetError());


    sp<Thread> pt(new ProducerThread(mANW));
        // Consume a frame
    pt->run();
        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
                mEglContext));
        ASSERT_EQ(EGL_SUCCESS, eglGetError());
        ASSERT_EQ(NO_ERROR, mST->updateTexImage());
        buffers[i] = mST->getCurrentBuffer();
    }


    fw->waitForFrame();
    // Abandon the SurfaceTexture, releasing the ref that the SurfaceTexture has
    fw->waitForFrame();
    // on buffers[2].
    mST->abandon();


    // Sleep for 100ms to allow the producer thread's dequeueBuffer call to
    // Destroy the GL texture object to release its ref on buffers[2].
    // block waiting for a buffer to become available.
    GLuint texID = TEX_ID;
    usleep(100000);
    glDeleteTextures(1, &texID);


    mST->abandon();
    // Destroy the EGLSurface.
    EXPECT_TRUE(eglDestroySurface(mEglDisplay, mProducerEglSurface));
    ASSERT_EQ(EGL_SUCCESS, eglGetError());


    pt->requestExitAndWait();
    EXPECT_EQ(1, buffers[0]->getStrongCount());
    ASSERT_EQ(NO_INIT,
    EXPECT_EQ(1, buffers[1]->getStrongCount());
            reinterpret_cast<ProducerThread*>(pt.get())->getDequeueError());
    EXPECT_EQ(1, buffers[2]->getStrongCount());
}
}


/*
/*
 * This test is for testing GL -> GL texture streaming via SurfaceTexture.  It
 * This test fixture is for testing GL -> GL texture streaming from one thread
 * contains functionality to create a producer thread that will perform GL
 * to another.  It contains functionality to create a producer thread that will
 * rendering to an ANativeWindow that feeds frames to a SurfaceTexture.
 * perform GL rendering to an ANativeWindow that feeds frames to a
 * Additionally it supports interlocking the producer and consumer threads so
 * SurfaceTexture.  Additionally it supports interlocking the producer and
 * that a specific sequence of calls can be deterministically created by the
 * consumer threads so that a specific sequence of calls can be
 * test.
 * deterministically created by the test.
 *
 *
 * The intended usage is as follows:
 * The intended usage is as follows:
 *
 *
@@ -1122,7 +1274,7 @@ TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) {
 * }
 * }
 *
 *
 */
 */
class SurfaceTextureGLToGLTest : public SurfaceTextureGLTest {
class SurfaceTextureGLThreadToGLTest : public SurfaceTextureGLToGLTest {
protected:
protected:


    // ProducerThread is an abstract base class to simplify the creation of
    // ProducerThread is an abstract base class to simplify the creation of
@@ -1223,30 +1375,8 @@ protected:
        Condition mFrameFinishCondition;
        Condition mFrameFinishCondition;
    };
    };


    SurfaceTextureGLToGLTest():
            mProducerEglSurface(EGL_NO_SURFACE),
            mProducerEglContext(EGL_NO_CONTEXT) {
    }

    virtual void SetUp() {
    virtual void SetUp() {
        SurfaceTextureGLTest::SetUp();
        SurfaceTextureGLToGLTest::SetUp();

        EGLConfig myConfig = {0};
        EGLint numConfigs = 0;
        EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &myConfig,
                1, &numConfigs));
        ASSERT_EQ(EGL_SUCCESS, eglGetError());

        mProducerEglSurface = eglCreateWindowSurface(mEglDisplay, myConfig,
                mANW.get(), NULL);
        ASSERT_EQ(EGL_SUCCESS, eglGetError());
        ASSERT_NE(EGL_NO_SURFACE, mProducerEglSurface);

        mProducerEglContext = eglCreateContext(mEglDisplay, myConfig,
                EGL_NO_CONTEXT, getContextAttribs());
        ASSERT_EQ(EGL_SUCCESS, eglGetError());
        ASSERT_NE(EGL_NO_CONTEXT, mProducerEglContext);

        mFC = new FrameCondition();
        mFC = new FrameCondition();
        mST->setFrameAvailableListener(mFC);
        mST->setFrameAvailableListener(mFC);
    }
    }
@@ -1255,15 +1385,9 @@ protected:
        if (mProducerThread != NULL) {
        if (mProducerThread != NULL) {
            mProducerThread->requestExitAndWait();
            mProducerThread->requestExitAndWait();
        }
        }
        if (mProducerEglContext != EGL_NO_CONTEXT) {
            eglDestroyContext(mEglDisplay, mProducerEglContext);
        }
        if (mProducerEglSurface != EGL_NO_SURFACE) {
            eglDestroySurface(mEglDisplay, mProducerEglSurface);
        }
        mProducerThread.clear();
        mProducerThread.clear();
        mFC.clear();
        mFC.clear();
        SurfaceTextureGLTest::TearDown();
        SurfaceTextureGLToGLTest::TearDown();
    }
    }


    void runProducerThread(const sp<ProducerThread> producerThread) {
    void runProducerThread(const sp<ProducerThread> producerThread) {
@@ -1274,13 +1398,12 @@ protected:
        producerThread->run();
        producerThread->run();
    }
    }


    EGLSurface mProducerEglSurface;
    EGLContext mProducerEglContext;
    sp<ProducerThread> mProducerThread;
    sp<ProducerThread> mProducerThread;
    sp<FrameCondition> mFC;
    sp<FrameCondition> mFC;
};
};


TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageBeforeFrameFinishedCompletes) {
TEST_F(SurfaceTextureGLThreadToGLTest,
        UpdateTexImageBeforeFrameFinishedCompletes) {
    class PT : public ProducerThread {
    class PT : public ProducerThread {
        virtual void render() {
        virtual void render() {
            glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
            glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
@@ -1298,7 +1421,8 @@ TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageBeforeFrameFinishedCompletes) {
    // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
    // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
}
}


TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageAfterFrameFinishedCompletes) {
TEST_F(SurfaceTextureGLThreadToGLTest,
        UpdateTexImageAfterFrameFinishedCompletes) {
    class PT : public ProducerThread {
    class PT : public ProducerThread {
        virtual void render() {
        virtual void render() {
            glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
            glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
@@ -1316,7 +1440,8 @@ TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageAfterFrameFinishedCompletes) {
    // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
    // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
}
}


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


    class PT : public ProducerThread {
    class PT : public ProducerThread {
@@ -1344,7 +1469,8 @@ TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageBeforeFrameFinishedComple
    }
    }
}
}


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


    class PT : public ProducerThread {
    class PT : public ProducerThread {
@@ -1373,7 +1499,8 @@ TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageAfterFrameFinishedComplet
}
}


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


    class PT : public ProducerThread {
    class PT : public ProducerThread {
@@ -1438,118 +1565,4 @@ TEST_F(SurfaceTextureGLToGLTest, DISABLED_RepeatedSwapBuffersWhileDequeueStalled
    }
    }
}
}


TEST_F(SurfaceTextureGLTest, EglDestroySurfaceUnrefsBuffers) {
    EGLSurface stcEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
            mANW.get(), NULL);
    ASSERT_EQ(EGL_SUCCESS, eglGetError());
    ASSERT_NE(EGL_NO_SURFACE, stcEglSurface);

    sp<GraphicBuffer> buffers[3];

    for (int i = 0; i < 3; i++) {
        // Produce a frame
        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, stcEglSurface, stcEglSurface,
                mEglContext));
        ASSERT_EQ(EGL_SUCCESS, eglGetError());
        glClear(GL_COLOR_BUFFER_BIT);
        eglSwapBuffers(mEglDisplay, stcEglSurface);

        // Consume a frame
        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
                mEglContext));
        ASSERT_EQ(EGL_SUCCESS, eglGetError());
        mST->updateTexImage();
        buffers[i] = mST->getCurrentBuffer();
    }

    // Destroy the GL texture object to release its ref on buffers[2].
    GLuint texID = TEX_ID;
    glDeleteTextures(1, &texID);

    // Destroy the EGLSurface
    EXPECT_TRUE(eglDestroySurface(mEglDisplay, stcEglSurface));
    ASSERT_EQ(EGL_SUCCESS, eglGetError());

    // Release the ref that the SurfaceTexture has on buffers[2].
    mST->abandon();

    EXPECT_EQ(1, buffers[0]->getStrongCount());
    EXPECT_EQ(1, buffers[1]->getStrongCount());
    EXPECT_EQ(1, buffers[2]->getStrongCount());
}

TEST_F(SurfaceTextureGLTest, EglDestroySurfaceAfterAbandonUnrefsBuffers) {
    EGLSurface stcEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
            mANW.get(), NULL);
    ASSERT_EQ(EGL_SUCCESS, eglGetError());
    ASSERT_NE(EGL_NO_SURFACE, stcEglSurface);

    sp<GraphicBuffer> buffers[3];

    for (int i = 0; i < 3; i++) {
        // Produce a frame
        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, stcEglSurface, stcEglSurface,
                mEglContext));
        ASSERT_EQ(EGL_SUCCESS, eglGetError());
        glClear(GL_COLOR_BUFFER_BIT);
        EXPECT_TRUE(eglSwapBuffers(mEglDisplay, stcEglSurface));
        ASSERT_EQ(EGL_SUCCESS, eglGetError());

        // Consume a frame
        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
                mEglContext));
        ASSERT_EQ(EGL_SUCCESS, eglGetError());
        ASSERT_EQ(NO_ERROR, mST->updateTexImage());
        buffers[i] = mST->getCurrentBuffer();
    }

    // Abandon the SurfaceTexture, releasing the ref that the SurfaceTexture has
    // on buffers[2].
    mST->abandon();

    // Destroy the GL texture object to release its ref on buffers[2].
    GLuint texID = TEX_ID;
    glDeleteTextures(1, &texID);

    // Destroy the EGLSurface.
    EXPECT_TRUE(eglDestroySurface(mEglDisplay, stcEglSurface));
    ASSERT_EQ(EGL_SUCCESS, eglGetError());

    EXPECT_EQ(1, buffers[0]->getStrongCount());
    EXPECT_EQ(1, buffers[1]->getStrongCount());
    EXPECT_EQ(1, buffers[2]->getStrongCount());
}

TEST_F(SurfaceTextureGLTest, InvalidWidthOrHeightFails) {
    int texHeight = 16;
    ANativeWindowBuffer* anb;

    GLint maxTextureSize;
    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);

    // make sure it works with small textures
    mST->setDefaultBufferSize(16, texHeight);
    EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
    EXPECT_EQ(16, anb->width);
    EXPECT_EQ(texHeight, anb->height);
    EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb));
    EXPECT_EQ(NO_ERROR, mST->updateTexImage());

    // make sure it works with GL_MAX_TEXTURE_SIZE
    mST->setDefaultBufferSize(maxTextureSize, texHeight);
    EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
    EXPECT_EQ(maxTextureSize, anb->width);
    EXPECT_EQ(texHeight, anb->height);
    EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb));
    EXPECT_EQ(NO_ERROR, mST->updateTexImage());

    // make sure it fails with GL_MAX_TEXTURE_SIZE+1
    mST->setDefaultBufferSize(maxTextureSize+1, texHeight);
    EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
    EXPECT_EQ(maxTextureSize+1, anb->width);
    EXPECT_EQ(texHeight, anb->height);
    EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb));
    ASSERT_NE(NO_ERROR, mST->updateTexImage());
}

} // namespace android
} // namespace android