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

Commit 72f6af86 authored by Jamie Gennis's avatar Jamie Gennis Committed by Android (Google) Code Review
Browse files

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.
parents 1033f1dc 5451d15e
Loading
Loading
Loading
Loading
+14 −3
Original line number Diff line number Diff line
@@ -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;

};

// ----------------------------------------------------------------------------
+8 −2
Original line number Diff line number Diff line
@@ -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() {
+108 −0
Original line number Diff line number Diff line
@@ -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
+268 −0
Original line number Diff line number Diff line
@@ -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