Loading include/gui/SurfaceTexture.h +22 −4 Original line number Diff line number Diff line Loading @@ -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(); Loading Loading @@ -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(); } Loading Loading @@ -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 Loading Loading @@ -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. Loading libs/gui/SurfaceTexture.cpp +233 −166 Original line number Diff line number Diff line Loading @@ -36,6 +36,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" Loading @@ -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__) Loading Loading @@ -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), Loading @@ -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. Loading Loading @@ -261,9 +282,12 @@ 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; { // Scope for the lock Mutex::Autolock lock(mMutex); int found = -1; int foundSync = -1; Loading Loading @@ -327,7 +351,8 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, // if buffer is FREE it CANNOT be current LOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i), "dequeueBuffer: buffer %d is both FREE and current!", i); "dequeueBuffer: buffer %d is both FREE and current!", i); if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) { if (state == BufferSlot::FREE || i == mCurrentTexture) { Loading @@ -339,11 +364,14 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, } } 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) { /* 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; } Loading @@ -359,8 +387,9 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, 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. // 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 Loading @@ -375,8 +404,8 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, } } // we're in synchronous mode and didn't find a buffer, we need to wait // for some buffers to be consumed // 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); Loading Loading @@ -436,8 +465,10 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, } 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); eglDestroyImageKHR(mSlots[buf].mEglDisplay, mSlots[buf].mEglImage); mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR; mSlots[buf].mEglDisplay = EGL_NO_DISPLAY; } Loading @@ -448,8 +479,28 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, } 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; } Loading Loading @@ -712,8 +763,8 @@ status_t SurfaceTexture::updateTexImage() { // Update the GL texture object. EGLImageKHR image = mSlots[buf].mEglImage; if (image == EGL_NO_IMAGE_KHR) { EGLDisplay dpy = eglGetCurrentDisplay(); if (image == EGL_NO_IMAGE_KHR) { if (mSlots[buf].mGraphicBuffer == 0) { ST_LOGE("buffer at slot %d is null", buf); return BAD_VALUE; Loading Loading @@ -746,17 +797,33 @@ 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. mCurrentTexture = buf; Loading libs/gui/tests/SurfaceTexture_test.cpp +111 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 services/surfaceflinger/SurfaceTextureLayer.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -28,7 +28,7 @@ namespace android { SurfaceTextureLayer::SurfaceTextureLayer(GLuint tex, const sp<Layer>& layer) : SurfaceTexture(tex), mLayer(layer) { : SurfaceTexture(tex, true, GL_TEXTURE_EXTERNAL_OES, false), mLayer(layer) { } SurfaceTextureLayer::~SurfaceTextureLayer() { Loading Loading
include/gui/SurfaceTexture.h +22 −4 Original line number Diff line number Diff line Loading @@ -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(); Loading Loading @@ -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(); } Loading Loading @@ -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 Loading Loading @@ -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. Loading
libs/gui/SurfaceTexture.cpp +233 −166 Original line number Diff line number Diff line Loading @@ -36,6 +36,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" Loading @@ -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__) Loading Loading @@ -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), Loading @@ -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. Loading Loading @@ -261,9 +282,12 @@ 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; { // Scope for the lock Mutex::Autolock lock(mMutex); int found = -1; int foundSync = -1; Loading Loading @@ -327,7 +351,8 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, // if buffer is FREE it CANNOT be current LOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i), "dequeueBuffer: buffer %d is both FREE and current!", i); "dequeueBuffer: buffer %d is both FREE and current!", i); if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) { if (state == BufferSlot::FREE || i == mCurrentTexture) { Loading @@ -339,11 +364,14 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, } } 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) { /* 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; } Loading @@ -359,8 +387,9 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, 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. // 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 Loading @@ -375,8 +404,8 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, } } // we're in synchronous mode and didn't find a buffer, we need to wait // for some buffers to be consumed // 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); Loading Loading @@ -436,8 +465,10 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, } 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); eglDestroyImageKHR(mSlots[buf].mEglDisplay, mSlots[buf].mEglImage); mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR; mSlots[buf].mEglDisplay = EGL_NO_DISPLAY; } Loading @@ -448,8 +479,28 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, } 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; } Loading Loading @@ -712,8 +763,8 @@ status_t SurfaceTexture::updateTexImage() { // Update the GL texture object. EGLImageKHR image = mSlots[buf].mEglImage; if (image == EGL_NO_IMAGE_KHR) { EGLDisplay dpy = eglGetCurrentDisplay(); if (image == EGL_NO_IMAGE_KHR) { if (mSlots[buf].mGraphicBuffer == 0) { ST_LOGE("buffer at slot %d is null", buf); return BAD_VALUE; Loading Loading @@ -746,17 +797,33 @@ 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. mCurrentTexture = buf; Loading
libs/gui/tests/SurfaceTexture_test.cpp +111 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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
services/surfaceflinger/SurfaceTextureLayer.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -28,7 +28,7 @@ namespace android { SurfaceTextureLayer::SurfaceTextureLayer(GLuint tex, const sp<Layer>& layer) : SurfaceTexture(tex), mLayer(layer) { : SurfaceTexture(tex, true, GL_TEXTURE_EXTERNAL_OES, false), mLayer(layer) { } SurfaceTextureLayer::~SurfaceTextureLayer() { Loading