diff options
| author | 2011-06-13 11:14:57 -0700 | |
|---|---|---|
| committer | 2011-06-13 11:14:57 -0700 | |
| commit | 72f6af86305cd7fa1db6ebedff10a28c3c4f26f4 (patch) | |
| tree | b3d1f0dca37bf597a5a0774c40d51401edd89d7f | |
| parent | 1033f1dc027229be9b5152a163546004b57dc76a (diff) | |
| parent | 5451d15ee209f29d64beea87583f7058dee69911 (diff) | |
Merge changes I56779420,I044e44e1,Ic5adfa29,Ied541ab8
* changes:
SurfaceTexture: add some GL->GL tests.
SurfaceTexture: fix up a comment.
SurfaceTexture: add getTransformMatrix tests.
SurfaceTexture: fix a getTransformMatrix crash.
| -rw-r--r-- | include/gui/SurfaceTexture.h | 17 | ||||
| -rw-r--r-- | libs/gui/SurfaceTexture.cpp | 10 | ||||
| -rw-r--r-- | libs/gui/tests/SurfaceTextureClient_test.cpp | 108 | ||||
| -rw-r--r-- | libs/gui/tests/SurfaceTexture_test.cpp | 268 |
4 files changed, 398 insertions, 5 deletions
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h index 2b314621ab..9294df6743 100644 --- a/include/gui/SurfaceTexture.h +++ b/include/gui/SurfaceTexture.h @@ -188,6 +188,11 @@ private: status_t setBufferCountServerLocked(int bufferCount); + // computeCurrentTransformMatrix computes the transform matrix for the + // current texture. It uses mCurrentTransform and the current GraphicBuffer + // to compute this matrix and stores it in mCurrentTransformMatrix. + void computeCurrentTransformMatrix(); + enum { INVALID_BUFFER_SLOT = -1 }; struct BufferSlot { @@ -288,9 +293,9 @@ private: // by calling setBufferCount or setBufferCountServer int mBufferCount; - // mRequestedBufferCount is the number of buffer slots requested by the - // client. The default is zero, which means the client doesn't care how - // many buffers there is. + // mClientBufferCount is the number of buffer slots requested by the client. + // The default is zero, which means the client doesn't care how many buffers + // there is. int mClientBufferCount; // mServerBufferCount buffer count requested by the server-side @@ -322,6 +327,11 @@ private: // gets set to mLastQueuedTransform each time updateTexImage is called. uint32_t mCurrentTransform; + // mCurrentTransformMatrix is the transform matrix for the current texture. + // It gets computed by computeTransformMatrix each time updateTexImage is + // called. + float mCurrentTransformMatrix[16]; + // mCurrentTimestamp is the timestamp for the current texture. It // gets set to mLastQueuedTimestamp each time updateTexImage is called. int64_t mCurrentTimestamp; @@ -362,6 +372,7 @@ private: // variables of SurfaceTexture objects. It must be locked whenever the // member variables are accessed. mutable Mutex mMutex; + }; // ---------------------------------------------------------------------------- diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index ee97dcfa9c..2cda4c82db 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -96,6 +96,7 @@ SurfaceTexture::SurfaceTexture(GLuint tex) : sp<ISurfaceComposer> composer(ComposerService::getComposerService()); mGraphicBufferAlloc = composer->createGraphicBufferAlloc(); mNextCrop.makeInvalid(); + memcpy(mCurrentTransformMatrix, mtxIdentity, sizeof(mCurrentTransformMatrix)); } SurfaceTexture::~SurfaceTexture() { @@ -547,6 +548,7 @@ status_t SurfaceTexture::updateTexImage() { mCurrentCrop = mSlots[buf].mCrop; mCurrentTransform = mSlots[buf].mTransform; mCurrentTimestamp = mSlots[buf].mTimestamp; + computeCurrentTransformMatrix(); mDequeueCondition.signal(); } else { // We always bind the texture even if we don't update its contents. @@ -596,8 +598,12 @@ GLenum SurfaceTexture::getCurrentTextureTarget() const { } void SurfaceTexture::getTransformMatrix(float mtx[16]) { - LOGV("SurfaceTexture::getTransformMatrix"); Mutex::Autolock lock(mMutex); + memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix)); +} + +void SurfaceTexture::computeCurrentTransformMatrix() { + LOGV("SurfaceTexture::computeCurrentTransformMatrix"); float xform[16]; for (int i = 0; i < 16; i++) { @@ -684,7 +690,7 @@ void SurfaceTexture::getTransformMatrix(float mtx[16]) { // coordinate of 0, so SurfaceTexture must behave the same way. We don't // want to expose this to applications, however, so we must add an // additional vertical flip to the transform after all the other transforms. - mtxMul(mtx, mtxFlipV, mtxBeforeFlipV); + mtxMul(mCurrentTransformMatrix, mtxFlipV, mtxBeforeFlipV); } nsecs_t SurfaceTexture::getTimestamp() { diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp index 2f704c89b8..da04b4a3e6 100644 --- a/libs/gui/tests/SurfaceTextureClient_test.cpp +++ b/libs/gui/tests/SurfaceTextureClient_test.cpp @@ -514,4 +514,112 @@ TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeWaitRetire) { thread->requestExitAndWait(); } +TEST_F(SurfaceTextureClientTest, GetTransformMatrixReturnsVerticalFlip) { + sp<ANativeWindow> anw(mSTC); + sp<SurfaceTexture> st(mST); + android_native_buffer_t* buf[3]; + float mtx[16] = {}; + ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4)); + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); + ASSERT_EQ(OK, st->updateTexImage()); + st->getTransformMatrix(mtx); + + EXPECT_EQ(1.f, mtx[0]); + EXPECT_EQ(0.f, mtx[1]); + EXPECT_EQ(0.f, mtx[2]); + EXPECT_EQ(0.f, mtx[3]); + + EXPECT_EQ(0.f, mtx[4]); + EXPECT_EQ(-1.f, mtx[5]); + EXPECT_EQ(0.f, mtx[6]); + EXPECT_EQ(0.f, mtx[7]); + + EXPECT_EQ(0.f, mtx[8]); + EXPECT_EQ(0.f, mtx[9]); + EXPECT_EQ(1.f, mtx[10]); + EXPECT_EQ(0.f, mtx[11]); + + EXPECT_EQ(0.f, mtx[12]); + EXPECT_EQ(1.f, mtx[13]); + EXPECT_EQ(0.f, mtx[14]); + EXPECT_EQ(1.f, mtx[15]); +} + +TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffers) { + sp<ANativeWindow> anw(mSTC); + sp<SurfaceTexture> st(mST); + android_native_buffer_t* buf[3]; + float mtx[16] = {}; + ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4)); + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); + ASSERT_EQ(OK, st->updateTexImage()); + ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 6)); // frees buffers + st->getTransformMatrix(mtx); + + EXPECT_EQ(1.f, mtx[0]); + EXPECT_EQ(0.f, mtx[1]); + EXPECT_EQ(0.f, mtx[2]); + EXPECT_EQ(0.f, mtx[3]); + + EXPECT_EQ(0.f, mtx[4]); + EXPECT_EQ(-1.f, mtx[5]); + EXPECT_EQ(0.f, mtx[6]); + EXPECT_EQ(0.f, mtx[7]); + + EXPECT_EQ(0.f, mtx[8]); + EXPECT_EQ(0.f, mtx[9]); + EXPECT_EQ(1.f, mtx[10]); + EXPECT_EQ(0.f, mtx[11]); + + EXPECT_EQ(0.f, mtx[12]); + EXPECT_EQ(1.f, mtx[13]); + EXPECT_EQ(0.f, mtx[14]); + EXPECT_EQ(1.f, mtx[15]); +} + +TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffersWithCrop) { + sp<ANativeWindow> anw(mSTC); + sp<SurfaceTexture> st(mST); + android_native_buffer_t* buf[3]; + float mtx[16] = {}; + android_native_rect_t crop; + crop.left = 0; + crop.top = 0; + crop.right = 5; + crop.bottom = 5; + + ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4)); + ASSERT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 8, 8, 0)); + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); + ASSERT_EQ(OK, native_window_set_crop(anw.get(), &crop)); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); + ASSERT_EQ(OK, st->updateTexImage()); + ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 6)); // frees buffers + st->getTransformMatrix(mtx); + + // This accounts for the 1 texel shrink for each edge that's included in the + // transform matrix to avoid texturing outside the crop region. + EXPECT_EQ(.5f, mtx[0]); + EXPECT_EQ(0.f, mtx[1]); + EXPECT_EQ(0.f, mtx[2]); + EXPECT_EQ(0.f, mtx[3]); + + EXPECT_EQ(0.f, mtx[4]); + EXPECT_EQ(-.5f, mtx[5]); + EXPECT_EQ(0.f, mtx[6]); + EXPECT_EQ(0.f, mtx[7]); + + EXPECT_EQ(0.f, mtx[8]); + EXPECT_EQ(0.f, mtx[9]); + EXPECT_EQ(1.f, mtx[10]); + EXPECT_EQ(0.f, mtx[11]); + + EXPECT_EQ(0.f, mtx[12]); + EXPECT_EQ(.5f, mtx[13]); + EXPECT_EQ(0.f, mtx[14]); + EXPECT_EQ(1.f, mtx[15]); } + +} // namespace android diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp index 8747ba5c7c..16280d2e01 100644 --- a/libs/gui/tests/SurfaceTexture_test.cpp +++ b/libs/gui/tests/SurfaceTexture_test.cpp @@ -14,11 +14,14 @@ * limitations under the License. */ +//#define LOG_NDEBUG 0 + #include <gtest/gtest.h> #include <gui/SurfaceTexture.h> #include <gui/SurfaceTextureClient.h> #include <ui/GraphicBuffer.h> #include <utils/String8.h> +#include <utils/threads.h> #include <surfaceflinger/ISurfaceComposer.h> #include <surfaceflinger/Surface.h> @@ -618,4 +621,269 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) { } } +/* + * This test is for testing GL -> GL texture streaming via SurfaceTexture. It + * contains functionality to create a producer thread that will perform GL + * rendering to an ANativeWindow that feeds frames to a SurfaceTexture. + * 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 SurfaceTextureGLToGLTest : public SurfaceTextureGLTest { +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 SurfaceTexture, + // and should call both waitForFrame and finishFrame once for each expected + // frame. + // + // This interlocking relies on the fact that onFrameAvailable gets called + // synchronously from SurfaceTexture::queueBuffer. + class FrameCondition : public SurfaceTexture::FrameAvailableListener { + public: + // 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() { + LOGV("+waitForFrame"); + Mutex::Autolock lock(mMutex); + status_t result = mFrameAvailableCondition.wait(mMutex); + LOGV("-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() { + LOGV("+finishFrame"); + Mutex::Autolock lock(mMutex); + mFrameFinishCondition.signal(); + LOGV("-finishFrame"); + } + + // This should be called by SurfaceTexture on the producer thread. + virtual void onFrameAvailable() { + LOGV("+onFrameAvailable"); + Mutex::Autolock lock(mMutex); + mFrameAvailableCondition.signal(); + mFrameFinishCondition.wait(mMutex); + LOGV("-onFrameAvailable"); + } + + protected: + Mutex mMutex; + Condition mFrameAvailableCondition; + Condition mFrameFinishCondition; + }; + + 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); + + mFC = new FrameCondition(); + mST->setFrameAvailableListener(mFC); + } + + virtual void TearDown() { + if (mProducerThread != NULL) { + mProducerThread->requestExitAndWait(); + } + if (mProducerEglContext != EGL_NO_CONTEXT) { + eglDestroyContext(mEglDisplay, mProducerEglContext); + } + if (mProducerEglSurface != EGL_NO_SURFACE) { + eglDestroySurface(mEglDisplay, mProducerEglSurface); + } + mProducerThread.clear(); + mFC.clear(); + } + + void runProducerThread(const sp<ProducerThread> producerThread) { + ASSERT_TRUE(mProducerThread == NULL); + mProducerThread = producerThread; + producerThread->setEglObjects(mEglDisplay, mProducerEglSurface, + mProducerEglContext); + producerThread->run(); + } + + EGLSurface mProducerEglSurface; + EGLContext mProducerEglContext; + sp<ProducerThread> mProducerThread; + sp<FrameCondition> mFC; +}; + +// XXX: This test is disabled because it causes hangs on some devices. +TEST_F(SurfaceTextureGLToGLTest, DISABLED_UpdateTexImageBeforeFrameFinishedWorks) { + 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(); + mST->updateTexImage(); + mFC->finishFrame(); + + // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! } + +TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageAfterFrameFinishedWorks) { + 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(); + mST->updateTexImage(); + + // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! +} + +// XXX: This test is disabled because it causes hangs on some devices. +TEST_F(SurfaceTextureGLToGLTest, DISABLED_RepeatedUpdateTexImageBeforeFrameFinishedWorks) { + 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); + LOGV("+swapBuffers"); + swapBuffers(); + LOGV("-swapBuffers"); + } + } + }; + + runProducerThread(new PT()); + + for (int i = 0; i < NUM_ITERATIONS; i++) { + mFC->waitForFrame(); + LOGV("+updateTexImage"); + mST->updateTexImage(); + LOGV("-updateTexImage"); + mFC->finishFrame(); + + // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! + } +} + +// XXX: This test is disabled because it causes hangs on some devices. +TEST_F(SurfaceTextureGLToGLTest, DISABLED_RepeatedUpdateTexImageAfterFrameFinishedWorks) { + 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); + LOGV("+swapBuffers"); + swapBuffers(); + LOGV("-swapBuffers"); + } + } + }; + + runProducerThread(new PT()); + + for (int i = 0; i < NUM_ITERATIONS; i++) { + mFC->waitForFrame(); + mFC->finishFrame(); + LOGV("+updateTexImage"); + mST->updateTexImage(); + LOGV("-updateTexImage"); + + // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! + } +} + +} // namespace android |