diff options
| -rw-r--r-- | include/gui/SurfaceTexture.h | 30 | ||||
| -rw-r--r-- | include/gui/SurfaceTextureClient.h | 1 | ||||
| -rw-r--r-- | include/ui/GraphicBuffer.h | 1 | ||||
| -rwxr-xr-x | include/ui/KeycodeLabels.h | 4 | ||||
| -rw-r--r-- | libs/binder/IPCThreadState.cpp | 1 | ||||
| -rw-r--r-- | libs/gui/Android.mk | 4 | ||||
| -rw-r--r-- | libs/gui/SurfaceTexture.cpp | 412 | ||||
| -rw-r--r-- | libs/gui/SurfaceTextureClient.cpp | 6 | ||||
| -rw-r--r-- | libs/gui/tests/SurfaceTexture_test.cpp | 611 | ||||
| -rw-r--r-- | opengl/libs/Android.mk | 9 | ||||
| -rw-r--r-- | opengl/libs/EGL/eglApi.cpp | 13 | ||||
| -rw-r--r-- | opengl/libs/EGL/egl_cache.cpp | 14 | ||||
| -rw-r--r-- | opengl/specs/README | 3 | ||||
| -rw-r--r-- | opengl/tools/glgen/stubs/gles11/GLES11ExtHeader.java-if | 6 | ||||
| -rw-r--r-- | services/surfaceflinger/Android.mk | 1 | ||||
| -rw-r--r-- | services/surfaceflinger/SurfaceFlinger.cpp | 20 | ||||
| -rw-r--r-- | services/surfaceflinger/SurfaceFlinger.h | 1 | ||||
| -rw-r--r-- | services/surfaceflinger/SurfaceTextureLayer.cpp | 2 |
18 files changed, 743 insertions, 396 deletions
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h index 27d863de58..a8c76725e7 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(); @@ -202,6 +208,10 @@ public: // getCurrentScalingMode returns the scaling mode of the current buffer uint32_t getCurrentScalingMode() const; + // isSynchronousMode returns whether the SurfaceTexture is currently in + // synchronous mode. + bool isSynchronousMode() const; + // abandon frees all the buffers and puts the SurfaceTexture into the // 'abandoned' state. Once put in this state the SurfaceTexture can never // leave it. When in the 'abandoned' state, all methods of the @@ -272,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(); } @@ -345,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 @@ -468,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/include/gui/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h index 57f9e151de..971a1b80dc 100644 --- a/include/gui/SurfaceTextureClient.h +++ b/include/gui/SurfaceTextureClient.h @@ -40,6 +40,7 @@ public: protected: SurfaceTextureClient(); + virtual ~SurfaceTextureClient(); void setISurfaceTexture(const sp<ISurfaceTexture>& surfaceTexture); private: diff --git a/include/ui/GraphicBuffer.h b/include/ui/GraphicBuffer.h index b9deafcd3b..6ab01f4c98 100644 --- a/include/ui/GraphicBuffer.h +++ b/include/ui/GraphicBuffer.h @@ -63,6 +63,7 @@ public: USAGE_HW_RENDER = GRALLOC_USAGE_HW_RENDER, USAGE_HW_2D = GRALLOC_USAGE_HW_2D, USAGE_HW_COMPOSER = GRALLOC_USAGE_HW_COMPOSER, + USAGE_HW_VIDEO_ENCODER = GRALLOC_USAGE_HW_VIDEO_ENCODER, USAGE_HW_MASK = GRALLOC_USAGE_HW_MASK }; diff --git a/include/ui/KeycodeLabels.h b/include/ui/KeycodeLabels.h index 2efe8ca0ee..c5bd0c5446 100755 --- a/include/ui/KeycodeLabels.h +++ b/include/ui/KeycodeLabels.h @@ -231,6 +231,10 @@ static const KeycodeLabel KEYCODES[] = { { "LANGUAGE_SWITCH", 204 }, { "MANNER_MODE", 205 }, { "3D_MODE", 206 }, + { "CONTACTS", 207 }, + { "CALENDAR", 208 }, + { "MUSIC", 209 }, + { "CALCULATOR", 210 }, // NOTE: If you add a new keycode here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 5ccf87f2cb..5d34787d6c 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -773,6 +773,7 @@ status_t IPCThreadState::talkWithDriver(bool doReceive) bwr.read_buffer = (long unsigned int)mIn.data(); } else { bwr.read_size = 0; + bwr.read_buffer = 0; } IF_LOG_COMMANDS() { diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk index ed319f5e93..9767568bed 100644 --- a/libs/gui/Android.mk +++ b/libs/gui/Android.mk @@ -32,6 +32,10 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_MODULE:= libgui +ifeq ($(TARGET_BOARD_PLATFORM), tegra) + LOCAL_CFLAGS += -DALLOW_DEQUEUE_CURRENT_BUFFER +endif + include $(BUILD_SHARED_LIBRARY) ifeq (,$(ONE_SHOT_MAKEFILE)) diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index 3c0d4756ff..fcd287c022 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -36,8 +36,28 @@ #include <utils/Log.h> #include <utils/String8.h> - -#define ALLOW_DEQUEUE_CURRENT_BUFFER false +// 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" +#else +#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__) @@ -95,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), @@ -112,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. @@ -257,190 +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; - } - - // 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 (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; + } - 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; + } + + 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; - } - 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; } @@ -637,8 +697,9 @@ status_t SurfaceTexture::disconnect(int api) { Mutex::Autolock lock(mMutex); if (mAbandoned) { - ST_LOGE("disconnect: SurfaceTexture has been abandoned!"); - return NO_INIT; + // it is not really an error to disconnect after the surface + // has been abandoned, it should just be a no-op. + return NO_ERROR; } int err = NO_ERROR; @@ -702,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; @@ -736,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. @@ -1000,6 +1077,11 @@ uint32_t SurfaceTexture::getCurrentScalingMode() const { return mCurrentScalingMode; } +bool SurfaceTexture::isSynchronousMode() const { + Mutex::Autolock lock(mMutex); + return mSynchronousMode; +} + int SurfaceTexture::query(int what, int* outValue) { Mutex::Autolock lock(mMutex); diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp index 98fa17174e..48070d67c3 100644 --- a/libs/gui/SurfaceTextureClient.cpp +++ b/libs/gui/SurfaceTextureClient.cpp @@ -36,6 +36,12 @@ SurfaceTextureClient::SurfaceTextureClient() { SurfaceTextureClient::init(); } +SurfaceTextureClient::~SurfaceTextureClient() { + if (mConnectedToCpu) { + SurfaceTextureClient::disconnect(NATIVE_WINDOW_API_CPU); + } +} + void SurfaceTextureClient::init() { // Initialize the ANativeWindow function pointers. ANativeWindow::setSwapInterval = hook_setSwapInterval; diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp index 93ebfb99ff..c313904283 100644 --- a/libs/gui/tests/SurfaceTexture_test.cpp +++ b/libs/gui/tests/SurfaceTexture_test.cpp @@ -396,7 +396,8 @@ protected: 1.0f, 1.0f, }; - glVertexAttribPointer(mPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, triangleVertices); + glVertexAttribPointer(mPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, + triangleVertices); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); glEnableVertexAttribArray(mPositionHandle); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); @@ -410,13 +411,17 @@ protected: // XXX: These calls are not needed for GL_TEXTURE_EXTERNAL_OES as // they're setting the defautls for that target, but when hacking things // to use GL_TEXTURE_2D they are needed to achieve the same behavior. - glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, + GL_LINEAR); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, + GL_LINEAR); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, + GL_CLAMP_TO_EDGE); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, + GL_CLAMP_TO_EDGE); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); GLfloat texMatrix[16]; @@ -531,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; @@ -640,8 +659,8 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) { for (int i = 0; i < 5; i++) { const android_native_rect_t& crop(crops[i]); - SCOPED_TRACE(String8::format("rect{ l: %d t: %d r: %d b: %d }", crop.left, - crop.top, crop.right, crop.bottom).string()); + SCOPED_TRACE(String8::format("rect{ l: %d t: %d r: %d b: %d }", + crop.left, crop.top, crop.right, crop.bottom).string()); ASSERT_EQ(NO_ERROR, native_window_set_crop(mANW.get(), &crop)); @@ -650,13 +669,15 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) { ASSERT_TRUE(anb != NULL); sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); - ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer())); + ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), + buf->getNativeBuffer())); uint8_t* img = NULL; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); fillYV12BufferRect(img, texWidth, texHeight, buf->getStride(), crop); buf->unlock(); - ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())); + ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), + buf->getNativeBuffer())); mST->updateTexImage(); @@ -708,7 +729,8 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) { class ProducerThread : public Thread { public: - ProducerThread(const sp<ANativeWindow>& anw, const TestPixel* testPixels): + ProducerThread(const sp<ANativeWindow>& anw, + const TestPixel* testPixels): mANW(anw), mTestPixels(testPixels) { } @@ -940,21 +962,173 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledRGBABufferPow2) { EXPECT_TRUE(checkPixel( 3, 52, 35, 231, 35, 35)); } -TEST_F(SurfaceTextureGLTest, TexturingFromGLFilledRGBABufferPow2) { +TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) { + class ProducerThread : public Thread { + public: + ProducerThread(const sp<ANativeWindow>& anw): + mANW(anw), + mDequeueError(NO_ERROR) { + } + + virtual ~ProducerThread() { + } + + virtual bool threadLoop() { + Mutex::Autolock lock(mMutex); + ANativeWindowBuffer* anb; + + // Frame 1 + if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) { + return false; + } + if (anb == NULL) { + return false; + } + if (mANW->queueBuffer(mANW.get(), anb) + != NO_ERROR) { + return false; + } + + // Frame 2 + if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) { + return false; + } + if (anb == NULL) { + return false; + } + if (mANW->queueBuffer(mANW.get(), anb) + != NO_ERROR) { + return false; + } + + // Frame 3 - error expected + mDequeueError = mANW->dequeueBuffer(mANW.get(), &anb); + return false; + } + + status_t getDequeueError() { + Mutex::Autolock lock(mMutex); + return mDequeueError; + } + + private: + sp<ANativeWindow> mANW; + status_t mDequeueError; + Mutex mMutex; + }; + + sp<FrameWaiter> fw(new FrameWaiter); + mST->setFrameAvailableListener(fw); + ASSERT_EQ(OK, mST->setSynchronousMode(true)); + ASSERT_EQ(OK, mST->setBufferCountServer(2)); + + sp<Thread> pt(new ProducerThread(mANW)); + pt->run(); + + fw->waitForFrame(); + fw->waitForFrame(); + + // Sleep for 100ms to allow the producer thread's dequeueBuffer call to + // block waiting for a buffer to become available. + usleep(100000); + + mST->abandon(); + + pt->requestExitAndWait(); + ASSERT_EQ(NO_INIT, + reinterpret_cast<ProducerThread*>(pt.get())->getDequeueError()); +} + +TEST_F(SurfaceTextureGLTest, InvalidWidthOrHeightFails) { + int texHeight = 16; + ANativeWindowBuffer* anb; + + GLint maxTextureSize; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); + + // make sure it works with small textures + mST->setDefaultBufferSize(16, texHeight); + EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + EXPECT_EQ(16, anb->width); + EXPECT_EQ(texHeight, anb->height); + EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb)); + EXPECT_EQ(NO_ERROR, mST->updateTexImage()); + + // make sure it works with GL_MAX_TEXTURE_SIZE + mST->setDefaultBufferSize(maxTextureSize, texHeight); + EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + EXPECT_EQ(maxTextureSize, anb->width); + EXPECT_EQ(texHeight, anb->height); + EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb)); + EXPECT_EQ(NO_ERROR, mST->updateTexImage()); + + // make sure it fails with GL_MAX_TEXTURE_SIZE+1 + mST->setDefaultBufferSize(maxTextureSize+1, texHeight); + EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + EXPECT_EQ(maxTextureSize+1, anb->width); + EXPECT_EQ(texHeight, anb->height); + EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb)); + ASSERT_NE(NO_ERROR, mST->updateTexImage()); +} + +/* + * This test fixture is for testing GL -> GL texture streaming. It creates an + * EGLSurface and an EGLContext for the image producer to use. + */ +class SurfaceTextureGLToGLTest : public SurfaceTextureGLTest { +protected: + 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); + } + + virtual void TearDown() { + if (mProducerEglContext != EGL_NO_CONTEXT) { + eglDestroyContext(mEglDisplay, mProducerEglContext); + } + if (mProducerEglSurface != EGL_NO_SURFACE) { + eglDestroySurface(mEglDisplay, mProducerEglSurface); + } + SurfaceTextureGLTest::TearDown(); + } + + EGLSurface mProducerEglSurface; + EGLContext mProducerEglContext; +}; + +TEST_F(SurfaceTextureGLToGLTest, TexturingFromGLFilledRGBABufferPow2) { const int texWidth = 64; const int texHeight = 64; mST->setDefaultBufferSize(texWidth, texHeight); // Do the producer side of things - EGLSurface stcEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, - mANW.get(), NULL); + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); - ASSERT_NE(EGL_NO_SURFACE, stcEglSurface); - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, stcEglSurface, stcEglSurface, - mEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); + // This is needed to ensure we pick up a buffer of the correct size. + eglSwapBuffers(mEglDisplay, mProducerEglSurface); glClearColor(0.6, 0.6, 0.6, 0.6); glClear(GL_COLOR_BUFFER_BIT); @@ -972,7 +1146,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromGLFilledRGBABufferPow2) { glClearColor(0.0, 0.0, 1.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); - eglSwapBuffers(mEglDisplay, stcEglSurface); + eglSwapBuffers(mEglDisplay, mProducerEglSurface); // Do the consumer side of things EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, @@ -981,12 +1155,9 @@ TEST_F(SurfaceTextureGLTest, TexturingFromGLFilledRGBABufferPow2) { glDisable(GL_SCISSOR_TEST); + mST->updateTexImage(); // Skip the first frame, which was empty mST->updateTexImage(); - // We must wait until updateTexImage has been called to destroy the - // EGLSurface because we're in synchronous mode. - eglDestroySurface(mEglDisplay, stcEglSurface); - glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); @@ -1016,90 +1187,136 @@ TEST_F(SurfaceTextureGLTest, TexturingFromGLFilledRGBABufferPow2) { EXPECT_TRUE(checkPixel( 3, 52, 153, 153, 153, 153)); } -TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) { - class ProducerThread : public Thread { - public: - ProducerThread(const sp<ANativeWindow>& anw): - mANW(anw), - mDequeueError(NO_ERROR) { - } +TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceUnrefsBuffers) { + sp<GraphicBuffer> buffers[3]; - virtual ~ProducerThread() { - } + // This test requires async mode to run on a single thread. + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + EXPECT_TRUE(eglSwapInterval(mEglDisplay, 0)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); - virtual bool threadLoop() { - Mutex::Autolock lock(mMutex); - ANativeWindowBuffer* anb; + for (int i = 0; i < 3; i++) { + // Produce a frame + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + glClear(GL_COLOR_BUFFER_BIT); + eglSwapBuffers(mEglDisplay, mProducerEglSurface); - // Frame 1 - if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) { - return false; - } - if (anb == NULL) { - return false; - } - if (mANW->queueBuffer(mANW.get(), anb) - != NO_ERROR) { - return false; - } + // Consume a frame + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + mST->updateTexImage(); + buffers[i] = mST->getCurrentBuffer(); + } - // Frame 2 - if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) { - return false; - } - if (anb == NULL) { - return false; - } - if (mANW->queueBuffer(mANW.get(), anb) - != NO_ERROR) { - return false; - } + // Destroy the GL texture object to release its ref on buffers[2]. + GLuint texID = TEX_ID; + glDeleteTextures(1, &texID); - // Frame 3 - error expected - mDequeueError = mANW->dequeueBuffer(mANW.get(), &anb); - return false; - } + // Destroy the EGLSurface + EXPECT_TRUE(eglDestroySurface(mEglDisplay, mProducerEglSurface)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); - status_t getDequeueError() { - Mutex::Autolock lock(mMutex); - return mDequeueError; - } + // Release the ref that the SurfaceTexture has on buffers[2]. + mST->abandon(); - private: - sp<ANativeWindow> mANW; - status_t mDequeueError; - Mutex mMutex; - }; + EXPECT_EQ(1, buffers[0]->getStrongCount()); + EXPECT_EQ(1, buffers[1]->getStrongCount()); - sp<FrameWaiter> fw(new FrameWaiter); - mST->setFrameAvailableListener(fw); - ASSERT_EQ(OK, mST->setSynchronousMode(true)); - ASSERT_EQ(OK, mST->setBufferCountServer(2)); + // Depending on how lazily the GL driver dequeues buffers, we may end up + // with either two or three total buffers. If there are three, make sure + // the last one was properly down-ref'd. + if (buffers[2] != buffers[0]) { + EXPECT_EQ(1, buffers[2]->getStrongCount()); + } +} - sp<Thread> pt(new ProducerThread(mANW)); - pt->run(); +TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceAfterAbandonUnrefsBuffers) { + sp<GraphicBuffer> buffers[3]; - fw->waitForFrame(); - fw->waitForFrame(); + // This test requires async mode to run on a single thread. + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + EXPECT_TRUE(eglSwapInterval(mEglDisplay, 0)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); - // Sleep for 100ms to allow the producer thread's dequeueBuffer call to - // block waiting for a buffer to become available. - usleep(100000); + for (int i = 0; i < 3; i++) { + // Produce a frame + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + glClear(GL_COLOR_BUFFER_BIT); + EXPECT_TRUE(eglSwapBuffers(mEglDisplay, mProducerEglSurface)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + // Consume a frame + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + buffers[i] = mST->getCurrentBuffer(); + } + // Abandon the SurfaceTexture, releasing the ref that the SurfaceTexture has + // on buffers[2]. mST->abandon(); - pt->requestExitAndWait(); - ASSERT_EQ(NO_INIT, - reinterpret_cast<ProducerThread*>(pt.get())->getDequeueError()); + // Destroy the GL texture object to release its ref on buffers[2]. + GLuint texID = TEX_ID; + glDeleteTextures(1, &texID); + + // Destroy the EGLSurface. + EXPECT_TRUE(eglDestroySurface(mEglDisplay, mProducerEglSurface)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + EXPECT_EQ(1, buffers[0]->getStrongCount()); + EXPECT_EQ(1, buffers[1]->getStrongCount()); + + // Depending on how lazily the GL driver dequeues buffers, we may end up + // with either two or three total buffers. If there are three, make sure + // the last one was properly down-ref'd. + if (buffers[2] != buffers[0]) { + EXPECT_EQ(1, buffers[2]->getStrongCount()); + } +} + +TEST_F(SurfaceTextureGLToGLTest, EglSurfaceDefaultsToSynchronousMode) { + // This test requires 3 buffers to run on a single thread. + mST->setBufferCountServer(3); + + ASSERT_TRUE(mST->isSynchronousMode()); + + for (int i = 0; i < 10; i++) { + // Produce a frame + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + glClear(GL_COLOR_BUFFER_BIT); + EXPECT_TRUE(eglSwapBuffers(mEglDisplay, mProducerEglSurface)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + // Consume a frame + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + } + + ASSERT_TRUE(mST->isSynchronousMode()); } /* - * 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. + * This test fixture is for testing GL -> GL texture streaming from one thread + * to another. 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: * @@ -1122,7 +1339,7 @@ TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) { * } * */ -class SurfaceTextureGLToGLTest : public SurfaceTextureGLTest { +class SurfaceTextureGLThreadToGLTest : public SurfaceTextureGLToGLTest { protected: // ProducerThread is an abstract base class to simplify the creation of @@ -1223,30 +1440,8 @@ protected: 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); - + SurfaceTextureGLToGLTest::SetUp(); mFC = new FrameCondition(); mST->setFrameAvailableListener(mFC); } @@ -1255,15 +1450,9 @@ protected: 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(); - SurfaceTextureGLTest::TearDown(); + SurfaceTextureGLToGLTest::TearDown(); } void runProducerThread(const sp<ProducerThread> producerThread) { @@ -1274,13 +1463,12 @@ protected: producerThread->run(); } - EGLSurface mProducerEglSurface; - EGLContext mProducerEglContext; sp<ProducerThread> mProducerThread; sp<FrameCondition> mFC; }; -TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageBeforeFrameFinishedCompletes) { +TEST_F(SurfaceTextureGLThreadToGLTest, + UpdateTexImageBeforeFrameFinishedCompletes) { class PT : public ProducerThread { virtual void render() { glClearColor(0.0f, 1.0f, 0.0f, 1.0f); @@ -1298,7 +1486,8 @@ TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageBeforeFrameFinishedCompletes) { // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! } -TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageAfterFrameFinishedCompletes) { +TEST_F(SurfaceTextureGLThreadToGLTest, + UpdateTexImageAfterFrameFinishedCompletes) { class PT : public ProducerThread { virtual void render() { glClearColor(0.0f, 1.0f, 0.0f, 1.0f); @@ -1316,7 +1505,8 @@ TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageAfterFrameFinishedCompletes) { // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! } -TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageBeforeFrameFinishedCompletes) { +TEST_F(SurfaceTextureGLThreadToGLTest, + RepeatedUpdateTexImageBeforeFrameFinishedCompletes) { enum { NUM_ITERATIONS = 1024 }; class PT : public ProducerThread { @@ -1344,7 +1534,8 @@ TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageBeforeFrameFinishedComple } } -TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageAfterFrameFinishedCompletes) { +TEST_F(SurfaceTextureGLThreadToGLTest, + RepeatedUpdateTexImageAfterFrameFinishedCompletes) { enum { NUM_ITERATIONS = 1024 }; class PT : public ProducerThread { @@ -1373,7 +1564,8 @@ TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageAfterFrameFinishedComplet } // XXX: This test is disabled because it is currently hanging on some devices. -TEST_F(SurfaceTextureGLToGLTest, DISABLED_RepeatedSwapBuffersWhileDequeueStalledCompletes) { +TEST_F(SurfaceTextureGLThreadToGLTest, + DISABLED_RepeatedSwapBuffersWhileDequeueStalledCompletes) { enum { NUM_ITERATIONS = 64 }; class PT : public ProducerThread { @@ -1438,118 +1630,101 @@ TEST_F(SurfaceTextureGLToGLTest, DISABLED_RepeatedSwapBuffersWhileDequeueStalled } } -TEST_F(SurfaceTextureGLTest, EglDestroySurfaceUnrefsBuffers) { - EGLSurface stcEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, - mANW.get(), NULL); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - ASSERT_NE(EGL_NO_SURFACE, stcEglSurface); +class SurfaceTextureFBOTest : public SurfaceTextureGLTest { +protected: - sp<GraphicBuffer> buffers[3]; + virtual void SetUp() { + SurfaceTextureGLTest::SetUp(); - for (int i = 0; i < 3; i++) { - // Produce a frame - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, stcEglSurface, stcEglSurface, - mEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - glClear(GL_COLOR_BUFFER_BIT); - eglSwapBuffers(mEglDisplay, stcEglSurface); + glGenFramebuffers(1, &mFbo); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - // Consume a frame - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - mST->updateTexImage(); - buffers[i] = mST->getCurrentBuffer(); + 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()); } - // Destroy the GL texture object to release its ref on buffers[2]. - GLuint texID = TEX_ID; - glDeleteTextures(1, &texID); + virtual void TearDown() { + SurfaceTextureGLTest::TearDown(); - // Destroy the EGLSurface - EXPECT_TRUE(eglDestroySurface(mEglDisplay, stcEglSurface)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); + glDeleteTextures(1, &mFboTex); + glDeleteFramebuffers(1, &mFbo); + } - // Release the ref that the SurfaceTexture has on buffers[2]. - mST->abandon(); + GLuint mFbo; + GLuint mFboTex; +}; - EXPECT_EQ(1, buffers[0]->getStrongCount()); - EXPECT_EQ(1, buffers[1]->getStrongCount()); - EXPECT_EQ(1, buffers[2]->getStrongCount()); -} +// 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; -TEST_F(SurfaceTextureGLTest, EglDestroySurfaceAfterAbandonUnrefsBuffers) { - EGLSurface stcEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, - mANW.get(), NULL); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - ASSERT_NE(EGL_NO_SURFACE, stcEglSurface); + 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)); - sp<GraphicBuffer> buffers[3]; + android_native_buffer_t* anb; + ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + ASSERT_TRUE(anb != NULL); - for (int i = 0; i < 3; i++) { - // Produce a frame - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, stcEglSurface, stcEglSurface, - mEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - glClear(GL_COLOR_BUFFER_BIT); - EXPECT_TRUE(eglSwapBuffers(mEglDisplay, stcEglSurface)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); + sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer())); - // Consume a frame - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - buffers[i] = mST->getCurrentBuffer(); - } + // 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())); - // Abandon the SurfaceTexture, releasing the ref that the SurfaceTexture has - // on buffers[2]. - mST->abandon(); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - // Destroy the GL texture object to release its ref on buffers[2]. - GLuint texID = TEX_ID; - glDeleteTextures(1, &texID); + glBindFramebuffer(GL_FRAMEBUFFER, mFbo); + drawTexture(); + glBindFramebuffer(GL_FRAMEBUFFER, 0); - // Destroy the EGLSurface. - EXPECT_TRUE(eglDestroySurface(mEglDisplay, stcEglSurface)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); + for (int i = 0; i < 4; i++) { + SCOPED_TRACE(String8::format("frame %d", i).string()); - EXPECT_EQ(1, buffers[0]->getStrongCount()); - EXPECT_EQ(1, buffers[1]->getStrongCount()); - EXPECT_EQ(1, buffers[2]->getStrongCount()); -} + ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + ASSERT_TRUE(anb != NULL); -TEST_F(SurfaceTextureGLTest, InvalidWidthOrHeightFails) { - int texHeight = 16; - ANativeWindowBuffer* anb; + buf = new GraphicBuffer(anb, false); + ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), + buf->getNativeBuffer())); - GLint maxTextureSize; - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); + // 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())); - // make sure it works with small textures - mST->setDefaultBufferSize(16, texHeight); - EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); - EXPECT_EQ(16, anb->width); - EXPECT_EQ(texHeight, anb->height); - EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb)); - EXPECT_EQ(NO_ERROR, mST->updateTexImage()); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - // make sure it works with GL_MAX_TEXTURE_SIZE - mST->setDefaultBufferSize(maxTextureSize, texHeight); - EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); - EXPECT_EQ(maxTextureSize, anb->width); - EXPECT_EQ(texHeight, anb->height); - EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb)); - EXPECT_EQ(NO_ERROR, mST->updateTexImage()); + drawTexture(); - // make sure it fails with GL_MAX_TEXTURE_SIZE+1 - mST->setDefaultBufferSize(maxTextureSize+1, texHeight); - EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); - EXPECT_EQ(maxTextureSize+1, anb->width); - EXPECT_EQ(texHeight, anb->height); - EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb)); - ASSERT_NE(NO_ERROR, mST->updateTexImage()); + EXPECT_TRUE(checkPixel( 24, 39, 255, 0, 0, 255)); + } + + glBindFramebuffer(GL_FRAMEBUFFER, mFbo); + + EXPECT_TRUE(checkPixel( 24, 39, 0, 255, 0, 255)); } } // namespace android diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk index 5855b635b0..9c1a10e214 100644 --- a/opengl/libs/Android.mk +++ b/opengl/libs/Android.mk @@ -44,10 +44,17 @@ ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true) LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER endif +ifneq ($(MAX_EGL_CACHE_ENTRY_SIZE),) + LOCAL_CFLAGS += -DMAX_EGL_CACHE_ENTRY_SIZE=$(MAX_EGL_CACHE_ENTRY_SIZE) +endif + +ifneq ($(MAX_EGL_CACHE_SIZE),) + LOCAL_CFLAGS += -DMAX_EGL_CACHE_SIZE=$(MAX_EGL_CACHE_SIZE) +endif + include $(BUILD_SHARED_LIBRARY) installed_libEGL := $(LOCAL_INSTALLED_MODULE) - # OpenGL drivers config file ifneq ($(BOARD_EGL_CFG),) diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp index 2237eb6c08..2b0ed5dfd6 100644 --- a/opengl/libs/EGL/eglApi.cpp +++ b/opengl/libs/EGL/eglApi.cpp @@ -49,6 +49,8 @@ using namespace android; // ---------------------------------------------------------------------------- +#define EGL_VERSION_HW_ANDROID 0x3143 + struct extention_map_t { const char* name; __eglMustCastToProperFunctionPointerType address; @@ -370,6 +372,11 @@ EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config, } } + // the EGL spec requires that a new EGLSurface default to swap interval + // 1, so explicitly set that on the window here. + ANativeWindow* anw = reinterpret_cast<ANativeWindow*>(window); + anw->setSwapInterval(anw, 1); + EGLSurface surface = cnx->egl.eglCreateWindowSurface( iDpy, iConfig, window, attrib_list); if (surface != EGL_NO_SURFACE) { @@ -967,6 +974,12 @@ const char* eglQueryString(EGLDisplay dpy, EGLint name) return dp->getExtensionString(); case EGL_CLIENT_APIS: return dp->getClientApiString(); + case EGL_VERSION_HW_ANDROID: { + if (gEGLImpl[IMPL_HARDWARE].dso) { + return dp->disp[IMPL_HARDWARE].queryString.version; + } + return dp->disp[IMPL_SOFTWARE].queryString.version; + } } return setError(EGL_BAD_PARAMETER, (const char *)0); } diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp index 13a492917d..c4a7466ec5 100644 --- a/opengl/libs/EGL/egl_cache.cpp +++ b/opengl/libs/EGL/egl_cache.cpp @@ -25,10 +25,18 @@ #include <sys/types.h> #include <unistd.h> +#ifndef MAX_EGL_CACHE_ENTRY_SIZE +#define MAX_EGL_CACHE_ENTRY_SIZE (16 * 1024); +#endif + +#ifndef MAX_EGL_CACHE_SIZE +#define MAX_EGL_CACHE_SIZE (64 * 1024); +#endif + // Cache size limits. static const size_t maxKeySize = 1024; -static const size_t maxValueSize = 4096; -static const size_t maxTotalSize = 64 * 1024; +static const size_t maxValueSize = MAX_EGL_CACHE_ENTRY_SIZE; +static const size_t maxTotalSize = MAX_EGL_CACHE_SIZE; // Cache file header static const char* cacheFileMagic = "EGL$"; @@ -85,7 +93,7 @@ void egl_cache_t::initialize(egl_display_t *display) { bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1); bool atEnd = (bcExtLen+1) < extsLen && !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1)); - bool inMiddle = strstr(" " BC_EXT_STR " ", exts); + bool inMiddle = strstr(exts, " " BC_EXT_STR " "); if (equal || atStart || atEnd || inMiddle) { PFNEGLSETBLOBCACHEFUNCSANDROIDPROC eglSetBlobCacheFuncsANDROID; eglSetBlobCacheFuncsANDROID = diff --git a/opengl/specs/README b/opengl/specs/README index 2fa258777f..16b278fd49 100644 --- a/opengl/specs/README +++ b/opengl/specs/README @@ -9,4 +9,5 @@ for use by Android extensions. 0x3140 EGL_ANDROID_image_native_buffer 0x3141 (unused) 0x3142 EGL_ANDROID_recordable -0x3143 - 0x314F (unused) +0x3143 EGL_VERSION_HW_ANDROID (internal use) +0x3144 - 0x314F (unused) diff --git a/opengl/tools/glgen/stubs/gles11/GLES11ExtHeader.java-if b/opengl/tools/glgen/stubs/gles11/GLES11ExtHeader.java-if index c5e34cd1b9..0c5fa04f13 100644 --- a/opengl/tools/glgen/stubs/gles11/GLES11ExtHeader.java-if +++ b/opengl/tools/glgen/stubs/gles11/GLES11ExtHeader.java-if @@ -124,6 +124,10 @@ public class GLES11Ext { public static final int GL_TEXTURE_MAX_ANISOTROPY_EXT = 0x84FE; public static final int GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT = 0x84FF; public static final int GL_BGRA = 0x80E1; + public static final int GL_TEXTURE_EXTERNAL_OES = 0x8D65; + public static final int GL_SAMPLER_EXTERNAL_OES = 0x8D66; + public static final int GL_TEXTURE_BINDING_EXTERNAL_OES = 0x8D67; + public static final int GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES = 0x8D68; native private static void _nativeClassInit(); static { @@ -135,4 +139,4 @@ public class GLES11Ext { private static final int GL_FLOAT = GLES10.GL_FLOAT; private static final int GL_SHORT = GLES10.GL_SHORT; - private static Buffer _matrixIndexPointerOES;
\ No newline at end of file + private static Buffer _matrixIndexPointerOES; diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk index 61a8358332..f63c0c12c2 100644 --- a/services/surfaceflinger/Android.mk +++ b/services/surfaceflinger/Android.mk @@ -28,6 +28,7 @@ ifeq ($(TARGET_BOARD_PLATFORM), omap4) endif ifeq ($(TARGET_BOARD_PLATFORM), s5pc110) LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY -DNEVER_DEFAULT_TO_ASYNC_MODE + LOCAL_CFLAGS += -DREFRESH_RATE=56 endif diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 1b00e9380a..24bd2a63ed 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -65,6 +65,8 @@ #define AID_GRAPHICS 1003 #endif +#define EGL_VERSION_HW_ANDROID 0x3143 + #define DISPLAY_COUNT 1 namespace android { @@ -708,6 +710,14 @@ void SurfaceFlinger::computeVisibleRegions( void SurfaceFlinger::commitTransaction() { + if (!mLayersPendingRemoval.isEmpty()) { + // Notify removed layers now that they can't be drawn from + for (size_t i = 0; i < mLayersPendingRemoval.size(); i++) { + mLayersPendingRemoval[i]->onRemoved(); + } + mLayersPendingRemoval.clear(); + } + mDrawingState = mCurrentState; mTransationPending = false; mTransactionCV.broadcast(); @@ -1160,7 +1170,7 @@ status_t SurfaceFlinger::purgatorizeLayer_l(const sp<LayerBase>& layerBase) mLayerPurgatory.add(layerBase); } - layerBase->onRemoved(); + mLayersPendingRemoval.push(layerBase); // it's possible that we don't find a layer, because it might // have been destroyed already -- this is not technically an error @@ -1527,7 +1537,7 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) * Dump the layers in the purgatory */ - const size_t purgatorySize = mLayerPurgatory.size(); + const size_t purgatorySize = mLayerPurgatory.size(); snprintf(buffer, SIZE, "Purgatory state (%d entries)\n", purgatorySize); result.append(buffer); for (size_t i=0 ; i<purgatorySize ; i++) { @@ -1548,6 +1558,12 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) extensions.getRenderer(), extensions.getVersion()); result.append(buffer); + + snprintf(buffer, SIZE, "EGL : %s\n", + eglQueryString(graphicPlane(0).getEGLDisplay(), + EGL_VERSION_HW_ANDROID)); + result.append(buffer); + snprintf(buffer, SIZE, "EXTS: %s\n", extensions.getExtension()); result.append(buffer); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 17028dbb64..17b80a6ed2 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -345,6 +345,7 @@ private: Condition mTransactionCV; SortedVector< sp<LayerBase> > mLayerPurgatory; bool mTransationPending; + Vector< sp<LayerBase> > mLayersPendingRemoval; // protected by mStateLock (but we could use another lock) GraphicPlane mGraphicPlanes[1]; diff --git a/services/surfaceflinger/SurfaceTextureLayer.cpp b/services/surfaceflinger/SurfaceTextureLayer.cpp index 4390ca19fc..5020e0007a 100644 --- a/services/surfaceflinger/SurfaceTextureLayer.cpp +++ b/services/surfaceflinger/SurfaceTextureLayer.cpp @@ -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() { |