From fe27e2f4688301629f38a06c34e3466c9889b3b1 Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Fri, 11 Nov 2011 18:05:11 -0800 Subject: [PATCH 1/2] SurfaceTexture: add a blit-to-FBO test This change adds a test for blitting (via GL rendering) from a SurfaceTexture to an FBO. Change-Id: Ib3386fcc3f37153277f3e37a26347441bb80ab58 --- libs/gui/tests/SurfaceTexture_test.cpp | 111 +++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp index 6d1b95126..c31390428 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 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 From 86edf4f6470ee0f108bf40d3c1d23bf0a78c9c38 Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Mon, 14 Nov 2011 14:51:01 -0800 Subject: [PATCH 2/2] SurfaceTexture: add EGL_KHR_fence_sync option This change adds a compile-time option for SurfaceTexture to use the EGL_KHR_fence_sync extension to synchronize access to Gralloc buffers. Bug: 5122031 Change-Id: I7e973a358631fff5308acf377581b811911fe790 --- include/gui/SurfaceTexture.h | 26 +- libs/gui/SurfaceTexture.cpp | 407 ++++++++++-------- .../surfaceflinger/SurfaceTextureLayer.cpp | 2 +- 3 files changed, 260 insertions(+), 175 deletions(-) diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h index 15c2bab59..a8c76725e 100644 --- a/include/gui/SurfaceTexture.h +++ b/include/gui/SurfaceTexture.h @@ -60,10 +60,16 @@ public: virtual void onFrameAvailable() = 0; }; - // tex indicates the name OpenGL texture to which images are to be streamed. - // This texture name cannot be changed once the SurfaceTexture is created. + // SurfaceTexture constructs a new SurfaceTexture object. tex indicates the + // name of the OpenGL ES texture to which images are to be streamed. This + // texture name cannot be changed once the SurfaceTexture is created. + // allowSynchronousMode specifies whether or not synchronous mode can be + // enabled. texTarget specifies the OpenGL ES texture target to which the + // texture will be bound in updateTexImage. useFenceSync specifies whether + // fences should be used to synchronize access to buffers if that behavior + // is enabled at compile-time. SurfaceTexture(GLuint tex, bool allowSynchronousMode = true, - GLenum texTarget = GL_TEXTURE_EXTERNAL_OES); + GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true); virtual ~SurfaceTexture(); @@ -276,7 +282,8 @@ private: mTransform(0), mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), mTimestamp(0), - mFrameNumber(0) { + mFrameNumber(0), + mFence(EGL_NO_SYNC_KHR) { mCrop.makeInvalid(); } @@ -349,6 +356,11 @@ private: // mFrameNumber is the number of the queued frame for this slot. uint64_t mFrameNumber; + // mFence is the EGL sync object that must signal before the buffer + // associated with this buffer slot may be dequeued. It is initialized + // to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based + // on a compile-time option) set to a new sync object in updateTexImage. + EGLSyncKHR mFence; }; // mSlots is the array of buffer slots that must be mirrored on the client @@ -472,6 +484,12 @@ private: // It is set by the setName method. String8 mName; + // mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync + // extension should be used to prevent buffers from being dequeued before + // it's safe for them to be written. It gets set at construction time and + // never changes. + const bool mUseFenceSync; + // mMutex is the mutex used to prevent concurrent access to the member // variables of SurfaceTexture objects. It must be locked whenever the // member variables are accessed. diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index beb23f640..fcd287c02 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -36,6 +36,12 @@ #include #include +// 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" @@ -43,6 +49,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, ...) LOGV("[%s] "x, mName.string(), ##__VA_ARGS__) #define ST_LOGD(x, ...) LOGD("[%s] "x, mName.string(), ##__VA_ARGS__) @@ -99,7 +115,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), @@ -116,6 +132,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. @@ -261,195 +282,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; - } + { // Scope for the lock + Mutex::Autolock lock(mMutex); - // 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 (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; - } - - // 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++; + 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; } - // if buffer is FREE it CANNOT be current - LOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i), - "dequeueBuffer: buffer %d is both FREE and current!", i); + // 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. - if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) { - if (state == BufferSlot::FREE || i == mCurrentTexture) { - foundSync = i; - if (i != mCurrentTexture) { - found = i; - break; - } + 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 (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; + } + + // 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; - } - - const int buf = found; - *outBuf = found; - - 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& 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( - mGraphicBufferAlloc->createGraphicBuffer( - w, h, format, usage, &error)); - if (graphicBuffer == 0) { - ST_LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer " - "failed"); - return error; + if (found == INVALID_BUFFER_SLOT) { + // This should not happen. + ST_LOGE("dequeueBuffer: no available buffer slots"); + return -EBUSY; } - if (updateFormat) { - mPixelFormat = format; + + const int buf = found; + *outBuf = found; + + const bool useDefaultSize = !w && !h; + if (useDefaultSize) { + // use the default size + w = mDefaultWidth; + h = mDefaultHeight; } - 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; + + const bool updateFormat = (format != 0); + if (!updateFormat) { + // keep the current (or default) format + format = mPixelFormat; } - 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; + + // 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& 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( + 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; } - returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION; + + dpy = mSlots[buf].mEglDisplay; + fence = mSlots[buf].mFence; + mSlots[buf].mFence = EGL_NO_SYNC_KHR; } + + 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"); + } + eglDestroySyncKHR(dpy, fence); + } + ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", buf, mSlots[buf].mGraphicBuffer->handle, returnFlags); + return returnFlags; } @@ -712,8 +763,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; @@ -746,16 +797,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/services/surfaceflinger/SurfaceTextureLayer.cpp b/services/surfaceflinger/SurfaceTextureLayer.cpp index 4390ca19f..5020e0007 100644 --- a/services/surfaceflinger/SurfaceTextureLayer.cpp +++ b/services/surfaceflinger/SurfaceTextureLayer.cpp @@ -28,7 +28,7 @@ namespace android { SurfaceTextureLayer::SurfaceTextureLayer(GLuint tex, const sp& layer) - : SurfaceTexture(tex), mLayer(layer) { + : SurfaceTexture(tex, true, GL_TEXTURE_EXTERNAL_OES, false), mLayer(layer) { } SurfaceTextureLayer::~SurfaceTextureLayer() {