summaryrefslogtreecommitdiffstats
path: root/libs
diff options
context:
space:
mode:
authorJamie Gennis <jgennis@google.com>2011-06-17 11:39:18 -0700
committerJamie Gennis <jgennis@google.com>2011-06-28 13:18:07 -0700
commit1b6230983186cca4977efe6a3c49261d327495cd (patch)
tree65edfbc26f9965d465ed1ef51b567b2ec44b44bc /libs
parent5b0eb1778af2d455ede77028884b336644ff90ff (diff)
downloadframeworks_base-1b6230983186cca4977efe6a3c49261d327495cd.zip
frameworks_base-1b6230983186cca4977efe6a3c49261d327495cd.tar.gz
frameworks_base-1b6230983186cca4977efe6a3c49261d327495cd.tar.bz2
SurfaceTexture: add a CPU -> GL sync test.
This change adds a test to verify that proper synchronization is done when using SurfaceTexture to use CPU generated images as OpenGL ES textures in 'synchronous mode'. Synchronous mode is particularly tricky because SurfaceTexture will allow the buffer that's currently bound to the GL texture to be dequeued. We rely on Gralloc's lock function to block until this buffer is no longer being used by the OpenGL ES. Change-Id: I1c4e37f3e9d60ab9a0905a90a1f5060fda8e485c
Diffstat (limited to 'libs')
-rw-r--r--libs/gui/tests/SurfaceTexture_test.cpp176
1 files changed, 176 insertions, 0 deletions
diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp
index f219639..dfa9211 100644
--- a/libs/gui/tests/SurfaceTexture_test.cpp
+++ b/libs/gui/tests/SurfaceTexture_test.cpp
@@ -419,6 +419,31 @@ protected:
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
}
+ class FrameWaiter : public SurfaceTexture::FrameAvailableListener {
+ public:
+ FrameWaiter():
+ mPendingFrames(0) {
+ }
+
+ void waitForFrame() {
+ Mutex::Autolock lock(mMutex);
+ while (mPendingFrames == 0) {
+ mCondition.wait(mMutex);
+ }
+ mPendingFrames--;
+ }
+
+ virtual void onFrameAvailable() {
+ Mutex::Autolock lock(mMutex);
+ mPendingFrames++;
+ mCondition.signal();
+ }
+
+ int mPendingFrames;
+ Mutex mMutex;
+ Condition mCondition;
+ };
+
sp<SurfaceTexture> mST;
sp<SurfaceTextureClient> mSTC;
sp<ANativeWindow> mANW;
@@ -648,6 +673,157 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) {
}
}
+// This test is intended to catch synchronization bugs between the CPU-written
+// and GPU-read buffers.
+TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) {
+ enum { texWidth = 16 };
+ enum { texHeight = 16 };
+ enum { numFrames = 1024 };
+
+ ASSERT_EQ(NO_ERROR, mST->setSynchronousMode(true));
+ ASSERT_EQ(NO_ERROR, mST->setBufferCountServer(2));
+ ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
+ texWidth, texHeight, HAL_PIXEL_FORMAT_YV12));
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
+ GRALLOC_USAGE_SW_WRITE_OFTEN));
+
+ struct TestPixel {
+ int x;
+ int y;
+ };
+ const TestPixel testPixels[] = {
+ { 4, 11 },
+ { 12, 14 },
+ { 7, 2 },
+ };
+ enum {numTestPixels = sizeof(testPixels) / sizeof(testPixels[0])};
+
+ class ProducerThread : public Thread {
+ public:
+ ProducerThread(const sp<ANativeWindow>& anw, const TestPixel* testPixels):
+ mANW(anw),
+ mTestPixels(testPixels) {
+ }
+
+ virtual ~ProducerThread() {
+ }
+
+ virtual bool threadLoop() {
+ for (int i = 0; i < numFrames; i++) {
+ ANativeWindowBuffer* anb;
+ if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) {
+ return false;
+ }
+ if (anb == NULL) {
+ return false;
+ }
+
+ sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+ if (mANW->lockBuffer(mANW.get(), buf->getNativeBuffer())
+ != NO_ERROR) {
+ return false;
+ }
+
+ const int yuvTexOffsetY = 0;
+ int stride = buf->getStride();
+ int yuvTexStrideY = stride;
+ int yuvTexOffsetV = yuvTexStrideY * texHeight;
+ int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf;
+ int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * texHeight/2;
+ int yuvTexStrideU = yuvTexStrideV;
+
+ uint8_t* img = NULL;
+ buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
+
+ // Gray out all the test pixels first, so we're more likely to
+ // see a failure if GL is still texturing from the buffer we
+ // just dequeued.
+ for (int j = 0; j < numTestPixels; j++) {
+ int x = mTestPixels[j].x;
+ int y = mTestPixels[j].y;
+ uint8_t value = 128;
+ img[y*stride + x] = value;
+ }
+
+ // Fill the buffer with gray.
+ for (int y = 0; y < texHeight; y++) {
+ for (int x = 0; x < texWidth; x++) {
+ img[yuvTexOffsetY + y*yuvTexStrideY + x] = 128;
+ img[yuvTexOffsetU + (y/2)*yuvTexStrideU + x/2] = 128;
+ img[yuvTexOffsetV + (y/2)*yuvTexStrideV + x/2] = 128;
+ }
+ }
+
+ // Set the test pixels to either white or black.
+ for (int j = 0; j < numTestPixels; j++) {
+ int x = mTestPixels[j].x;
+ int y = mTestPixels[j].y;
+ uint8_t value = 0;
+ if (j == (i % numTestPixels)) {
+ value = 255;
+ }
+ img[y*stride + x] = value;
+ }
+
+ buf->unlock();
+ if (mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())
+ != NO_ERROR) {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ sp<ANativeWindow> mANW;
+ const TestPixel* mTestPixels;
+ };
+
+ sp<FrameWaiter> fw(new FrameWaiter);
+ mST->setFrameAvailableListener(fw);
+
+ sp<Thread> pt(new ProducerThread(mANW, testPixels));
+ pt->run();
+
+ glViewport(0, 0, texWidth, texHeight);
+
+ glClearColor(0.2, 0.2, 0.2, 0.2);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ // We wait for the first two frames up front so that the producer will be
+ // likely to dequeue the buffer that's currently being textured from.
+ fw->waitForFrame();
+ fw->waitForFrame();
+
+ for (int i = 0; i < numFrames; i++) {
+ SCOPED_TRACE(String8::format("frame %d", i).string());
+
+ // We must wait for each frame to come in because if we ever do an
+ // updateTexImage call that doesn't consume a newly available buffer
+ // then the producer and consumer will get out of sync, which will cause
+ // a deadlock.
+ if (i > 1) {
+ fw->waitForFrame();
+ }
+ mST->updateTexImage();
+ drawTexture();
+
+ for (int j = 0; j < numTestPixels; j++) {
+ int x = testPixels[j].x;
+ int y = testPixels[j].y;
+ uint8_t value = 0;
+ if (j == (i % numTestPixels)) {
+ // We must y-invert the texture coords
+ EXPECT_TRUE(checkPixel(x, texHeight-y-1, 255, 255, 255, 255));
+ } else {
+ // We must y-invert the texture coords
+ EXPECT_TRUE(checkPixel(x, texHeight-y-1, 0, 0, 0, 255));
+ }
+ }
+ }
+
+ pt->requestExitAndWait();
+}
+
// XXX: This test is disabled because there are currently no drivers that can
// handle RGBA textures with the GL_TEXTURE_EXTERNAL_OES target.
TEST_F(SurfaceTextureGLTest, DISABLED_TexturingFromCpuFilledRGBABufferNpot) {