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.
This commit is contained in:
commit
72f6af8630
@ -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;
|
||||
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user