diff options
author | Mathias Agopian <mathias@google.com> | 2011-11-30 10:50:52 -0800 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2011-11-30 10:50:52 -0800 |
commit | a4fbecd1198899a3a4e55795406a06e7c111b13e (patch) | |
tree | 2c7c7d491a3baf34446824d029b838ba7f76a3cb /libs | |
parent | 27d22dc8625d4ccc2304a125de320f7ac49e2552 (diff) | |
parent | 53233c52d04d20813760f655f0ec7ed28657825b (diff) | |
download | frameworks_native-a4fbecd1198899a3a4e55795406a06e7c111b13e.zip frameworks_native-a4fbecd1198899a3a4e55795406a06e7c111b13e.tar.gz frameworks_native-a4fbecd1198899a3a4e55795406a06e7c111b13e.tar.bz2 |
am 55d3880e: am bb1e7d43: Merge changes I7e973a35,Ib3386fcc into ics-mr1
* commit '55d3880eed3450748eb7b97281e030902ee29c2a':
SurfaceTexture: add EGL_KHR_fence_sync option
SurfaceTexture: add a blit-to-FBO test
Diffstat (limited to 'libs')
-rw-r--r-- | libs/gui/SurfaceTexture.cpp | 399 | ||||
-rw-r--r-- | libs/gui/tests/SurfaceTexture_test.cpp | 111 |
2 files changed, 344 insertions, 166 deletions
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index 289726b..6f3051a 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -38,6 +38,12 @@ #include <utils/Log.h> #include <utils/String8.h> +// This compile option causes SurfaceTexture to return the buffer that is currently +// attached to the GL texture from dequeueBuffer when no other buffers are +// available. It requires the drivers (Gralloc, GL, OMX IL, and Camera) to do +// implicit cross-process synchronization to prevent the buffer from being +// written to before the buffer has (a) been detached from the GL texture and +// (b) all GL reads from the buffer have completed. #ifdef ALLOW_DEQUEUE_CURRENT_BUFFER #define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER true #warning "ALLOW_DEQUEUE_CURRENT_BUFFER enabled" @@ -45,6 +51,16 @@ #define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER false #endif +// This compile option makes SurfaceTexture use the EGL_KHR_fence_sync extension +// to synchronize access to the buffers. It will cause dequeueBuffer to stall, +// waiting for the GL reads for the buffer being dequeued to complete before +// allowing the buffer to be dequeued. +#ifdef USE_FENCE_SYNC +#ifdef ALLOW_DEQUEUE_CURRENT_BUFFER +#error "USE_FENCE_SYNC and ALLOW_DEQUEUE_CURRENT_BUFFER are incompatible" +#endif +#endif + // Macros for including the SurfaceTexture name in log messages #define ST_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__) #define ST_LOGD(x, ...) LOGD("[%s] "x, mName.string(), ##__VA_ARGS__) @@ -101,7 +117,7 @@ static int32_t createProcessUniqueId() { } SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode, - GLenum texTarget) : + GLenum texTarget, bool useFenceSync) : mDefaultWidth(1), mDefaultHeight(1), mPixelFormat(PIXEL_FORMAT_RGBA_8888), @@ -118,6 +134,11 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode, mAllowSynchronousMode(allowSynchronousMode), mConnectedApi(NO_CONNECTED_API), mAbandoned(false), +#ifdef USE_FENCE_SYNC + mUseFenceSync(useFenceSync), +#else + mUseFenceSync(false), +#endif mTexTarget(texTarget), mFrameCounter(0) { // Choose a name using the PID and a process-unique ID. @@ -263,195 +284,225 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, return BAD_VALUE; } - Mutex::Autolock lock(mMutex); - status_t returnFlags(OK); + EGLDisplay dpy = EGL_NO_DISPLAY; + EGLSyncKHR fence = EGL_NO_SYNC_KHR; - int found = -1; - int foundSync = -1; - int dequeuedCount = 0; - bool tryAgain = true; - while (tryAgain) { - if (mAbandoned) { - ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - - // We need to wait for the FIFO to drain if the number of buffer - // needs to change. - // - // The condition "number of buffers needs to change" is true if - // - the client doesn't care about how many buffers there are - // - AND the actual number of buffer is different from what was - // set in the last setBufferCountServer() - // - OR - - // setBufferCountServer() was set to a value incompatible with - // the synchronization mode (for instance because the sync mode - // changed since) - // - // As long as this condition is true AND the FIFO is not empty, we - // wait on mDequeueCondition. - - const int minBufferCountNeeded = mSynchronousMode ? - MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; - - const bool numberOfBuffersNeedsToChange = !mClientBufferCount && - ((mServerBufferCount != mBufferCount) || - (mServerBufferCount < minBufferCountNeeded)); - - if (!mQueue.isEmpty() && numberOfBuffersNeedsToChange) { - // wait for the FIFO to drain - mDequeueCondition.wait(mMutex); - // NOTE: we continue here because we need to reevaluate our - // whole state (eg: we could be abandoned or disconnected) - continue; - } + { // Scope for the lock + Mutex::Autolock lock(mMutex); - if (numberOfBuffersNeedsToChange) { - // here we're guaranteed that mQueue is empty - freeAllBuffersLocked(); - mBufferCount = mServerBufferCount; - if (mBufferCount < minBufferCountNeeded) - mBufferCount = minBufferCountNeeded; - mCurrentTexture = INVALID_BUFFER_SLOT; - returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS; - } + int found = -1; + int foundSync = -1; + int dequeuedCount = 0; + bool tryAgain = true; + while (tryAgain) { + if (mAbandoned) { + ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!"); + return NO_INIT; + } - // look for a free buffer to give to the client - found = INVALID_BUFFER_SLOT; - foundSync = INVALID_BUFFER_SLOT; - dequeuedCount = 0; - for (int i = 0; i < mBufferCount; i++) { - const int state = mSlots[i].mBufferState; - if (state == BufferSlot::DEQUEUED) { - dequeuedCount++; + // We need to wait for the FIFO to drain if the number of buffer + // needs to change. + // + // The condition "number of buffers needs to change" is true if + // - the client doesn't care about how many buffers there are + // - AND the actual number of buffer is different from what was + // set in the last setBufferCountServer() + // - OR - + // setBufferCountServer() was set to a value incompatible with + // the synchronization mode (for instance because the sync mode + // changed since) + // + // As long as this condition is true AND the FIFO is not empty, we + // wait on mDequeueCondition. + + const int minBufferCountNeeded = mSynchronousMode ? + MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; + + const bool numberOfBuffersNeedsToChange = !mClientBufferCount && + ((mServerBufferCount != mBufferCount) || + (mServerBufferCount < minBufferCountNeeded)); + + if (!mQueue.isEmpty() && numberOfBuffersNeedsToChange) { + // wait for the FIFO to drain + mDequeueCondition.wait(mMutex); + // NOTE: we continue here because we need to reevaluate our + // whole state (eg: we could be abandoned or disconnected) + continue; } - // if buffer is FREE it CANNOT be current - LOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i), - "dequeueBuffer: buffer %d is both FREE and current!", i); + if (numberOfBuffersNeedsToChange) { + // here we're guaranteed that mQueue is empty + freeAllBuffersLocked(); + mBufferCount = mServerBufferCount; + if (mBufferCount < minBufferCountNeeded) + mBufferCount = minBufferCountNeeded; + mCurrentTexture = INVALID_BUFFER_SLOT; + returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS; + } - if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) { - if (state == BufferSlot::FREE || i == mCurrentTexture) { - foundSync = i; - if (i != mCurrentTexture) { - found = i; - break; - } + // look for a free buffer to give to the client + found = INVALID_BUFFER_SLOT; + foundSync = INVALID_BUFFER_SLOT; + dequeuedCount = 0; + for (int i = 0; i < mBufferCount; i++) { + const int state = mSlots[i].mBufferState; + if (state == BufferSlot::DEQUEUED) { + dequeuedCount++; } - } else { - if (state == BufferSlot::FREE) { - /** For Asynchronous mode, we need to return the oldest of free buffers - * There is only one instance when the Framecounter overflows, this logic - * might return the earlier buffer to client. Which is a negligible impact - **/ - if (found < 0 || mSlots[i].mFrameNumber < mSlots[found].mFrameNumber) { + + // if buffer is FREE it CANNOT be current + LOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i), + "dequeueBuffer: buffer %d is both FREE and current!", + i); + + if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) { + if (state == BufferSlot::FREE || i == mCurrentTexture) { foundSync = i; - found = i; + if (i != mCurrentTexture) { + found = i; + break; + } + } + } else { + if (state == BufferSlot::FREE) { + /* We return the oldest of the free buffers to avoid + * stalling the producer if possible. This is because + * the consumer may still have pending reads of the + * buffers in flight. + */ + bool isOlder = mSlots[i].mFrameNumber < + mSlots[found].mFrameNumber; + if (found < 0 || isOlder) { + foundSync = i; + found = i; + } } } } - } - // clients are not allowed to dequeue more than one buffer - // if they didn't set a buffer count. - if (!mClientBufferCount && dequeuedCount) { - ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without " - "setting the buffer count"); - return -EINVAL; - } + // clients are not allowed to dequeue more than one buffer + // if they didn't set a buffer count. + if (!mClientBufferCount && dequeuedCount) { + ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without " + "setting the buffer count"); + return -EINVAL; + } + + // See whether a buffer has been queued since the last + // setBufferCount so we know whether to perform the + // MIN_UNDEQUEUED_BUFFERS check below. + bool bufferHasBeenQueued = mCurrentTexture != INVALID_BUFFER_SLOT; + if (bufferHasBeenQueued) { + // make sure the client is not trying to dequeue more buffers + // than allowed. + const int avail = mBufferCount - (dequeuedCount+1); + if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) { + ST_LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded " + "(dequeued=%d)", + MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode), + dequeuedCount); + return -EBUSY; + } + } - // See whether a buffer has been queued since the last setBufferCount so - // we know whether to perform the MIN_UNDEQUEUED_BUFFERS check below. - bool bufferHasBeenQueued = mCurrentTexture != INVALID_BUFFER_SLOT; - if (bufferHasBeenQueued) { - // make sure the client is not trying to dequeue more buffers - // than allowed. - const int avail = mBufferCount - (dequeuedCount+1); - if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) { - ST_LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded " - "(dequeued=%d)", - MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode), - dequeuedCount); - return -EBUSY; + // we're in synchronous mode and didn't find a buffer, we need to + // wait for some buffers to be consumed + tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT); + if (tryAgain) { + mDequeueCondition.wait(mMutex); } } - // we're in synchronous mode and didn't find a buffer, we need to wait - // for some buffers to be consumed - tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT); - if (tryAgain) { - mDequeueCondition.wait(mMutex); + if (mSynchronousMode && found == INVALID_BUFFER_SLOT) { + // foundSync guaranteed to be != INVALID_BUFFER_SLOT + found = foundSync; } - } - if (mSynchronousMode && found == INVALID_BUFFER_SLOT) { - // foundSync guaranteed to be != INVALID_BUFFER_SLOT - found = foundSync; - } + if (found == INVALID_BUFFER_SLOT) { + // This should not happen. + ST_LOGE("dequeueBuffer: no available buffer slots"); + return -EBUSY; + } - if (found == INVALID_BUFFER_SLOT) { - // This should not happen. - ST_LOGE("dequeueBuffer: no available buffer slots"); - return -EBUSY; - } + const int buf = found; + *outBuf = found; - const int buf = found; - *outBuf = found; + const bool useDefaultSize = !w && !h; + if (useDefaultSize) { + // use the default size + w = mDefaultWidth; + h = mDefaultHeight; + } - const bool useDefaultSize = !w && !h; - if (useDefaultSize) { - // use the default size - w = mDefaultWidth; - h = mDefaultHeight; - } + const bool updateFormat = (format != 0); + if (!updateFormat) { + // keep the current (or default) format + format = mPixelFormat; + } + + // buffer is now in DEQUEUED (but can also be current at the same time, + // if we're in synchronous mode) + mSlots[buf].mBufferState = BufferSlot::DEQUEUED; + + const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer); + if ((buffer == NULL) || + (uint32_t(buffer->width) != w) || + (uint32_t(buffer->height) != h) || + (uint32_t(buffer->format) != format) || + ((uint32_t(buffer->usage) & usage) != usage)) + { + usage |= GraphicBuffer::USAGE_HW_TEXTURE; + status_t error; + sp<GraphicBuffer> graphicBuffer( + mGraphicBufferAlloc->createGraphicBuffer( + w, h, format, usage, &error)); + if (graphicBuffer == 0) { + ST_LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer " + "failed"); + return error; + } + if (updateFormat) { + mPixelFormat = format; + } + mSlots[buf].mGraphicBuffer = graphicBuffer; + mSlots[buf].mRequestBufferCalled = false; + mSlots[buf].mFence = EGL_NO_SYNC_KHR; + if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(mSlots[buf].mEglDisplay, + mSlots[buf].mEglImage); + mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR; + mSlots[buf].mEglDisplay = EGL_NO_DISPLAY; + } + if (mCurrentTexture == buf) { + // The current texture no longer references the buffer in this slot + // since we just allocated a new buffer. + mCurrentTexture = INVALID_BUFFER_SLOT; + } + returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION; + } - const bool updateFormat = (format != 0); - if (!updateFormat) { - // keep the current (or default) format - format = mPixelFormat; + dpy = mSlots[buf].mEglDisplay; + fence = mSlots[buf].mFence; + mSlots[buf].mFence = EGL_NO_SYNC_KHR; } - // buffer is now in DEQUEUED (but can also be current at the same time, - // if we're in synchronous mode) - mSlots[buf].mBufferState = BufferSlot::DEQUEUED; - - const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer); - if ((buffer == NULL) || - (uint32_t(buffer->width) != w) || - (uint32_t(buffer->height) != h) || - (uint32_t(buffer->format) != format) || - ((uint32_t(buffer->usage) & usage) != usage)) - { - usage |= GraphicBuffer::USAGE_HW_TEXTURE; - status_t error; - sp<GraphicBuffer> graphicBuffer( - mGraphicBufferAlloc->createGraphicBuffer( - w, h, format, usage, &error)); - if (graphicBuffer == 0) { - ST_LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer " - "failed"); - return error; - } - if (updateFormat) { - mPixelFormat = format; + if (fence != EGL_NO_SYNC_KHR) { + EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); + // If something goes wrong, log the error, but return the buffer without + // synchronizing access to it. It's too late at this point to abort the + // dequeue operation. + if (result == EGL_FALSE) { + LOGE("dequeueBuffer: error waiting for fence: %#x", eglGetError()); + } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { + LOGE("dequeueBuffer: timeout waiting for fence"); } - mSlots[buf].mGraphicBuffer = graphicBuffer; - mSlots[buf].mRequestBufferCalled = false; - if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) { - eglDestroyImageKHR(mSlots[buf].mEglDisplay, mSlots[buf].mEglImage); - mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR; - mSlots[buf].mEglDisplay = EGL_NO_DISPLAY; - } - if (mCurrentTexture == buf) { - // The current texture no longer references the buffer in this slot - // since we just allocated a new buffer. - mCurrentTexture = INVALID_BUFFER_SLOT; - } - returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION; + eglDestroySyncKHR(dpy, fence); } + ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", buf, mSlots[buf].mGraphicBuffer->handle, returnFlags); + return returnFlags; } @@ -714,8 +765,8 @@ status_t SurfaceTexture::updateTexImage() { // Update the GL texture object. EGLImageKHR image = mSlots[buf].mEglImage; + EGLDisplay dpy = eglGetCurrentDisplay(); if (image == EGL_NO_IMAGE_KHR) { - EGLDisplay dpy = eglGetCurrentDisplay(); if (mSlots[buf].mGraphicBuffer == 0) { ST_LOGE("buffer at slot %d is null", buf); return BAD_VALUE; @@ -748,16 +799,32 @@ status_t SurfaceTexture::updateTexImage() { return -EINVAL; } - ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture, - mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, buf, - mSlots[buf].mGraphicBuffer->handle); + if (mCurrentTexture != INVALID_BUFFER_SLOT) { + if (mUseFenceSync) { + EGLSyncKHR fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, + NULL); + if (fence == EGL_NO_SYNC_KHR) { + LOGE("updateTexImage: error creating fence: %#x", + eglGetError()); + return -EINVAL; + } + glFlush(); + mSlots[mCurrentTexture].mFence = fence; + } + } + + ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)", + mCurrentTexture, + mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, + buf, mSlots[buf].mGraphicBuffer->handle); if (mCurrentTexture != INVALID_BUFFER_SLOT) { // The current buffer becomes FREE if it was still in the queued // state. If it has already been given to the client // (synchronous mode), then it stays in DEQUEUED state. - if (mSlots[mCurrentTexture].mBufferState == BufferSlot::QUEUED) + if (mSlots[mCurrentTexture].mBufferState == BufferSlot::QUEUED) { mSlots[mCurrentTexture].mBufferState = BufferSlot::FREE; + } } // Update the SurfaceTexture state. diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp index c79e69a..b18e7b0 100644 --- a/libs/gui/tests/SurfaceTexture_test.cpp +++ b/libs/gui/tests/SurfaceTexture_test.cpp @@ -536,6 +536,20 @@ void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride) { } } +void fillRGBA8BufferSolid(uint8_t* buf, int w, int h, int stride, uint8_t r, + uint8_t g, uint8_t b, uint8_t a) { + const size_t PIXEL_SIZE = 4; + for (int y = 0; y < h; y++) { + for (int x = 0; x < h; x++) { + off_t offset = (y * stride + x) * PIXEL_SIZE; + buf[offset + 0] = r; + buf[offset + 1] = g; + buf[offset + 2] = b; + buf[offset + 3] = a; + } + } +} + TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferNpot) { const int texWidth = 64; const int texHeight = 66; @@ -1616,4 +1630,101 @@ TEST_F(SurfaceTextureGLThreadToGLTest, } } +class SurfaceTextureFBOTest : public SurfaceTextureGLTest { +protected: + + virtual void SetUp() { + SurfaceTextureGLTest::SetUp(); + + glGenFramebuffers(1, &mFbo); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + + glGenTextures(1, &mFboTex); + glBindTexture(GL_TEXTURE_2D, mFboTex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getSurfaceWidth(), + getSurfaceHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glBindTexture(GL_TEXTURE_2D, 0); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + + glBindFramebuffer(GL_FRAMEBUFFER, mFbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, mFboTex, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + } + + virtual void TearDown() { + SurfaceTextureGLTest::TearDown(); + + glDeleteTextures(1, &mFboTex); + glDeleteFramebuffers(1, &mFbo); + } + + GLuint mFbo; + GLuint mFboTex; +}; + +// This test is intended to verify that proper synchronization is done when +// rendering into an FBO. +TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) { + const int texWidth = 64; + const int texHeight = 64; + + ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), + texWidth, texHeight, HAL_PIXEL_FORMAT_RGBA_8888)); + ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); + + android_native_buffer_t* anb; + ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + ASSERT_TRUE(anb != NULL); + + sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer())); + + // Fill the buffer with green + uint8_t* img = NULL; + buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); + fillRGBA8BufferSolid(img, texWidth, texHeight, buf->getStride(), 0, 255, + 0, 255); + buf->unlock(); + ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())); + + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + + glBindFramebuffer(GL_FRAMEBUFFER, mFbo); + drawTexture(); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + for (int i = 0; i < 4; i++) { + SCOPED_TRACE(String8::format("frame %d", i).string()); + + ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + ASSERT_TRUE(anb != NULL); + + buf = new GraphicBuffer(anb, false); + ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), + buf->getNativeBuffer())); + + // Fill the buffer with red + ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, + (void**)(&img))); + fillRGBA8BufferSolid(img, texWidth, texHeight, buf->getStride(), 255, 0, + 0, 255); + ASSERT_EQ(NO_ERROR, buf->unlock()); + ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), + buf->getNativeBuffer())); + + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + + drawTexture(); + + EXPECT_TRUE(checkPixel( 24, 39, 255, 0, 0, 255)); + } + + glBindFramebuffer(GL_FRAMEBUFFER, mFbo); + + EXPECT_TRUE(checkPixel( 24, 39, 0, 255, 0, 255)); +} + } // namespace android |