From 736aa9573bb7b78f9c315f396c104491b3639426 Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Sun, 12 Jun 2011 17:03:06 -0700 Subject: SurfaceTexture: fix a getTransformMatrix crash. This change moves the computation of the transform matrix out of getTransformMatrix and instead performs the computation when updateTexImage gets called. This is needed in order for getTransformMatrix to succeed even if the buffers have been freed (e.g. by changing the buffer count) because the computation depends upon the properties of the current GraphicBuffer. Change-Id: Ied541ab8747b7ad604f862717135f9a16a816be9 Bug: 4490420 --- include/gui/SurfaceTexture.h | 11 +++++++++++ libs/gui/SurfaceTexture.cpp | 10 ++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h index 2b314621ab..62c3a4a811 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 { @@ -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 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() { -- cgit v1.2.3-59-g8ed1b From 52226048095c2eb27ede0c3389626df270a0abff Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Tue, 7 Jun 2011 15:23:23 -0700 Subject: SurfaceTexture: add getTransformMatrix tests. This change adds a test verifying that with no transform set on the SurfaceTextureClient, the SurfaceTexture will return an identity transform matrix. It also verifies this same effect in the presence of an additional call to native_window_set_buffer_count just before the call to getTransformMatrix. Bug: 4490420 Change-Id: Ic5adfa29b5696cc2b451433834e3758ef20c5edd --- libs/gui/tests/SurfaceTextureClient_test.cpp | 108 +++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) 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 anw(mSTC); + sp 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 anw(mSTC); + sp 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 anw(mSTC); + sp 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 -- cgit v1.2.3-59-g8ed1b From ae468f43418c3cbae74c7f546283f6bb4e4df690 Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Sun, 12 Jun 2011 14:11:39 -0700 Subject: SurfaceTexture: fix up a comment. Change-Id: I044e44e17319e08af657a7ec9fa39100876fe862 --- include/gui/SurfaceTexture.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h index 62c3a4a811..9294df6743 100644 --- a/include/gui/SurfaceTexture.h +++ b/include/gui/SurfaceTexture.h @@ -293,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 -- cgit v1.2.3-59-g8ed1b From 5451d15ee209f29d64beea87583f7058dee69911 Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Wed, 8 Jun 2011 09:40:45 -0700 Subject: SurfaceTexture: add some GL->GL tests. This change adds some tests to verify streaming images from one GL thread to another via SurfaceTexture. Currently the tests do not validate the correctness of the streamed images, but rather simply verify that this streaming does not cause a deadlock. 3 of the 4 tests are currently disabled because they do cause deadlocks on some devices. Change-Id: I5677942053190063b97f370dba96c116711bc3bb --- libs/gui/tests/SurfaceTexture_test.cpp | 268 +++++++++++++++++++++++++++++++++ 1 file changed, 268 insertions(+) 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 #include #include #include #include +#include #include #include @@ -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) { + ASSERT_TRUE(mProducerThread == NULL); + mProducerThread = producerThread; + producerThread->setEglObjects(mEglDisplay, mProducerEglSurface, + mProducerEglContext); + producerThread->run(); + } + + EGLSurface mProducerEglSurface; + EGLContext mProducerEglContext; + sp mProducerThread; + sp 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 -- cgit v1.2.3-59-g8ed1b