Refactor SurfaceTexture a bit.
Rearranges updateTexImage() so that the SurfaceFlinger-specific
behavior is in a new SurfaceFlingerConsumer subclass.
SurfaceTexture behavior should not be altered. Instead of
acquire-bind-release we now do acquire-release-bind, but since
it's all done with the lock held there shouldn't be any
externally-visible change.
Change-Id: Ia566e4727945e2cfb9359fc6d2a8f8af64d7b7b7
diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h
index 9e265ba..88bcd8f 100644
--- a/include/gui/BufferQueue.h
+++ b/include/gui/BufferQueue.h
@@ -182,8 +182,9 @@
mBuf(INVALID_BUFFER_SLOT) {
mCrop.makeInvalid();
}
- // mGraphicBuffer points to the buffer allocated for this slot or is NULL
- // if no buffer has been allocated.
+ // mGraphicBuffer points to the buffer allocated for this slot, or is NULL
+ // if the buffer in this slot has been acquired in the past (see
+ // BufferSlot.mAcquireCalled).
sp<GraphicBuffer> mGraphicBuffer;
// mCrop is the current crop rectangle for this buffer slot.
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index b498a5c..a8b7d74 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -74,14 +74,13 @@
GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true,
const sp<BufferQueue> &bufferQueue = 0);
- // updateTexImage sets the image contents of the target texture to that of
- // the most recently queued buffer.
+ // updateTexImage acquires the most recently queued buffer, and sets the
+ // image contents of the target texture to it.
//
// This call may only be made while the OpenGL ES context to which the
// target texture belongs is bound to the calling thread.
//
- // After calling this method the doGLFenceWait method must be called
- // before issuing OpenGL ES commands that access the texture contents.
+ // This calls doGLFenceWait to ensure proper synchronization.
status_t updateTexImage();
// setReleaseFence stores a fence file descriptor that will signal when the
@@ -161,8 +160,7 @@
// doGLFenceWait inserts a wait command into the OpenGL ES command stream
// to ensure that it is safe for future OpenGL ES commands to access the
- // current texture buffer. This must be called each time updateTexImage
- // is called before issuing OpenGL ES commands that access the texture.
+ // current texture buffer.
status_t doGLFenceWait() const;
// isSynchronousMode returns whether the SurfaceTexture is currently in
@@ -233,23 +231,33 @@
virtual status_t releaseBufferLocked(int buf, EGLDisplay display,
EGLSyncKHR eglFence);
+ status_t releaseBufferLocked(int buf, EGLSyncKHR eglFence) {
+ return releaseBufferLocked(buf, mEglDisplay, eglFence);
+ }
+
static bool isExternalFormat(uint32_t format);
-private:
- // this version of updateTexImage() takes a functor used to reject or not
- // the newly acquired buffer.
- // this API is TEMPORARY and intended to be used by SurfaceFlinger only,
- // which is why class Layer is made a friend of SurfaceTexture below.
- class BufferRejecter {
- friend class SurfaceTexture;
- virtual bool reject(const sp<GraphicBuffer>& buf,
- const BufferQueue::BufferItem& item) = 0;
- protected:
- virtual ~BufferRejecter() { }
- };
- friend class Layer;
- status_t updateTexImage(BufferRejecter* rejecter, bool skipSync);
+ // This releases the buffer in the slot referenced by mCurrentTexture,
+ // then updates state to refer to the BufferItem, which must be a
+ // newly-acquired buffer.
+ status_t releaseAndUpdateLocked(const BufferQueue::BufferItem& item);
+ // Binds mTexName and the current buffer to mTexTarget. Uses
+ // mCurrentTexture if it's set, mCurrentTextureBuf if not.
+ status_t bindTextureImage();
+
+ // doGLFenceWaitLocked inserts a wait command into the OpenGL ES command
+ // stream to ensure that it is safe for future OpenGL ES commands to
+ // access the current texture buffer.
+ status_t doGLFenceWaitLocked() const;
+
+ // Gets the current EGLDisplay and EGLContext values, and compares them
+ // to mEglDisplay and mEglContext. If the fields have been previously
+ // set, the values must match; if not, the fields are set to the current
+ // values.
+ status_t checkAndUpdateEglStateLocked();
+
+private:
// createImage creates a new EGLImage from a GraphicBuffer.
EGLImageKHR createImage(EGLDisplay dpy,
const sp<GraphicBuffer>& graphicBuffer);
@@ -267,19 +275,19 @@
// mCurrentTextureBuf must not be NULL.
void computeCurrentTransformMatrixLocked();
- // doGLFenceWaitLocked inserts a wait command into the OpenGL ES command
- // stream to ensure that it is safe for future OpenGL ES commands to
- // access the current texture buffer. This must be called each time
- // updateTexImage is called before issuing OpenGL ES commands that access
- // the texture.
- status_t doGLFenceWaitLocked() const;
-
// syncForReleaseLocked performs the synchronization needed to release the
// current slot from an OpenGL ES context. If needed it will set the
// current slot's fence to guard against a producer accessing the buffer
// before the outstanding accesses have completed.
status_t syncForReleaseLocked(EGLDisplay dpy);
+ // Normally, when we bind a buffer to a texture target, we bind a buffer
+ // that is referenced by an entry in mEglSlots. In some situations we
+ // have a buffer in mCurrentTextureBuf, but no corresponding entry for
+ // it in our slot array. bindUnslottedBuffer handles that situation by
+ // binding the buffer without touching the EglSlots.
+ status_t bindUnslottedBufferLocked(EGLDisplay dpy);
+
// The default consumer usage flags that SurfaceTexture always sets on its
// BufferQueue instance; these will be OR:d with any additional flags passed
// from the SurfaceTexture user. In particular, SurfaceTexture will always
@@ -344,8 +352,8 @@
// EGLSlot contains the information and object references that
// SurfaceTexture maintains about a BufferQueue buffer slot.
- struct EGLSlot {
- EGLSlot()
+ struct EglSlot {
+ EglSlot()
: mEglImage(EGL_NO_IMAGE_KHR),
mEglFence(EGL_NO_SYNC_KHR) {
}
@@ -379,7 +387,7 @@
// slot that has not yet been used. The buffer allocated to a slot will also
// be replaced if the requested buffer usage or geometry differs from that
// of the buffer allocated to a slot.
- EGLSlot mEglSlots[BufferQueue::NUM_BUFFER_SLOTS];
+ EglSlot mEglSlots[BufferQueue::NUM_BUFFER_SLOTS];
// mCurrentTexture is the buffer slot index of the buffer that is currently
// bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index 086e298..609e7d2 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -29,7 +29,6 @@
#include <private/gui/ComposerService.h>
#include <utils/Log.h>
-#include <gui/SurfaceTexture.h>
#include <utils/Trace.h>
// Macros for including the BufferQueue name in log messages
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index b4dfb5e..037f5fb 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -154,7 +154,56 @@
}
status_t SurfaceTexture::updateTexImage() {
- return SurfaceTexture::updateTexImage(NULL, false);
+ ATRACE_CALL();
+ ST_LOGV("updateTexImage");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ ST_LOGE("updateTexImage: SurfaceTexture is abandoned!");
+ return NO_INIT;
+ }
+
+ // Make sure the EGL state is the same as in previous calls.
+ status_t err = checkAndUpdateEglStateLocked();
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ BufferQueue::BufferItem item;
+
+ // Acquire the next buffer.
+ // In asynchronous mode the list is guaranteed to be one buffer
+ // deep, while in synchronous mode we use the oldest buffer.
+ err = acquireBufferLocked(&item);
+ if (err != NO_ERROR) {
+ if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
+ // We always bind the texture even if we don't update its contents.
+ ST_LOGV("updateTexImage: no buffers were available");
+ glBindTexture(mTexTarget, mTexName);
+ err = NO_ERROR;
+ } else {
+ ST_LOGE("updateTexImage: acquire failed: %s (%d)",
+ strerror(-err), err);
+ }
+ return err;
+ }
+
+ // Release the previous buffer.
+ err = releaseAndUpdateLocked(item);
+ if (err != NO_ERROR) {
+ // We always bind the texture.
+ glBindTexture(mTexTarget, mTexName);
+ return err;
+ }
+
+ // Bind the new buffer to the GL texture.
+ err = bindTextureImage();
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ // Wait for the new buffer to be ready.
+ return doGLFenceWaitLocked();
}
status_t SurfaceTexture::acquireBufferLocked(BufferQueue::BufferItem *item) {
@@ -165,161 +214,156 @@
int slot = item->mBuf;
if (item->mGraphicBuffer != NULL) {
+ // This buffer has not been acquired before, so we must assume
+ // that any EGLImage in mEglSlots is stale.
if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) {
- eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage);
+ if (!eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage)) {
+ ST_LOGW("acquireBufferLocked: eglDestroyImageKHR failed for slot=%d",
+ slot);
+ // keep going
+ }
mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR;
}
}
- // Update the GL texture object. We may have to do this even when
- // item.mGraphicBuffer == NULL, if we destroyed the EGLImage when
- // detaching from a context but the buffer has not been re-allocated.
- if (mEglSlots[slot].mEglImage == EGL_NO_IMAGE_KHR) {
- EGLImageKHR image = createImage(mEglDisplay, mSlots[slot].mGraphicBuffer);
- if (image == EGL_NO_IMAGE_KHR) {
- return UNKNOWN_ERROR;
- }
- mEglSlots[slot].mEglImage = image;
- }
-
return NO_ERROR;
}
status_t SurfaceTexture::releaseBufferLocked(int buf, EGLDisplay display,
EGLSyncKHR eglFence) {
- status_t err = ConsumerBase::releaseBufferLocked(buf, mEglDisplay,
- eglFence);
+ status_t err = ConsumerBase::releaseBufferLocked(buf, display, eglFence);
mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR;
return err;
}
-status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter, bool skipSync) {
- ATRACE_CALL();
- ST_LOGV("updateTexImage");
- Mutex::Autolock lock(mMutex);
-
+status_t SurfaceTexture::releaseAndUpdateLocked(const BufferQueue::BufferItem& item)
+{
status_t err = NO_ERROR;
- if (mAbandoned) {
- ST_LOGE("updateTexImage: SurfaceTexture is abandoned!");
- return NO_INIT;
- }
-
if (!mAttached) {
- ST_LOGE("updateTexImage: SurfaceTexture is not attached to an OpenGL "
+ ST_LOGE("releaseAndUpdate: SurfaceTexture is not attached to an OpenGL "
"ES context");
return INVALID_OPERATION;
}
+ // Confirm state.
+ err = checkAndUpdateEglStateLocked();
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ int buf = item.mBuf;
+
+ // If the mEglSlot entry is empty, create an EGLImage for the gralloc
+ // buffer currently in the slot in ConsumerBase.
+ //
+ // We may have to do this even when item.mGraphicBuffer == NULL (which
+ // means the buffer was previously acquired), if we destroyed the
+ // EGLImage when detaching from a context but the buffer has not been
+ // re-allocated.
+ if (mEglSlots[buf].mEglImage == EGL_NO_IMAGE_KHR) {
+ EGLImageKHR image = createImage(mEglDisplay, mSlots[buf].mGraphicBuffer);
+ if (image == EGL_NO_IMAGE_KHR) {
+ ST_LOGW("releaseAndUpdate: unable to createImage on display=%p slot=%d",
+ mEglDisplay, buf);
+ return UNKNOWN_ERROR;
+ }
+ mEglSlots[buf].mEglImage = image;
+ }
+
+ // Do whatever sync ops we need to do before releasing the old slot.
+ err = syncForReleaseLocked(mEglDisplay);
+ if (err != NO_ERROR) {
+ // Release the buffer we just acquired. It's not safe to
+ // release the old buffer, so instead we just drop the new frame.
+ releaseBufferLocked(buf, mEglDisplay, EGL_NO_SYNC_KHR);
+ return err;
+ }
+
+ ST_LOGV("releaseAndUpdate: (slot=%d buf=%p) -> (slot=%d buf=%p)",
+ mCurrentTexture,
+ mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
+ buf, mSlots[buf].mGraphicBuffer->handle);
+
+ // release old buffer
+ if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+ status_t status = releaseBufferLocked(mCurrentTexture, mEglDisplay,
+ mEglSlots[mCurrentTexture].mEglFence);
+ if (status != NO_ERROR && status != BufferQueue::STALE_BUFFER_SLOT) {
+ ST_LOGE("releaseAndUpdate: failed to release buffer: %s (%d)",
+ strerror(-status), status);
+ err = status;
+ // keep going, with error raised [?]
+ }
+ }
+
+ // Update the SurfaceTexture state.
+ mCurrentTexture = buf;
+ mCurrentTextureBuf = mSlots[buf].mGraphicBuffer;
+ mCurrentCrop = item.mCrop;
+ mCurrentTransform = item.mTransform;
+ mCurrentScalingMode = item.mScalingMode;
+ mCurrentTimestamp = item.mTimestamp;
+ mCurrentFence = item.mFence;
+
+ computeCurrentTransformMatrixLocked();
+
+ return err;
+}
+
+status_t SurfaceTexture::bindTextureImage() {
+ if (mEglDisplay == EGL_NO_DISPLAY) {
+ ALOGE("bindTextureImage: invalid display");
+ return INVALID_OPERATION;
+ }
+
+ GLint error;
+ while ((error = glGetError()) != GL_NO_ERROR) {
+ ST_LOGW("bindTextureImage: clearing GL error: %#04x", error);
+ }
+
+ glBindTexture(mTexTarget, mTexName);
+ if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) {
+ if (mCurrentTextureBuf == NULL) {
+ ST_LOGE("bindTextureImage: no currently-bound texture");
+ return NO_INIT;
+ }
+ return bindUnslottedBufferLocked(mEglDisplay);
+ } else {
+ EGLImageKHR image = mEglSlots[mCurrentTexture].mEglImage;
+
+ glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
+
+ while ((error = glGetError()) != GL_NO_ERROR) {
+ ST_LOGE("bindTextureImage: error binding external texture image %p"
+ ": %#04x", image, error);
+ return UNKNOWN_ERROR;
+ }
+ return NO_ERROR;
+ }
+}
+
+status_t SurfaceTexture::checkAndUpdateEglStateLocked() {
EGLDisplay dpy = eglGetCurrentDisplay();
EGLContext ctx = eglGetCurrentContext();
if ((mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) ||
dpy == EGL_NO_DISPLAY) {
- ST_LOGE("updateTexImage: invalid current EGLDisplay");
+ ST_LOGE("checkAndUpdateEglState: invalid current EGLDisplay");
return INVALID_OPERATION;
}
if ((mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) ||
ctx == EGL_NO_CONTEXT) {
- ST_LOGE("updateTexImage: invalid current EGLContext");
+ ST_LOGE("checkAndUpdateEglState: invalid current EGLContext");
return INVALID_OPERATION;
}
mEglDisplay = dpy;
mEglContext = ctx;
-
- BufferQueue::BufferItem item;
-
- // In asynchronous mode the list is guaranteed to be one buffer
- // deep, while in synchronous mode we use the oldest buffer.
- err = acquireBufferLocked(&item);
- if (err == NO_ERROR) {
- int buf = item.mBuf;
-
- // we call the rejecter here, in case the caller has a reason to
- // not accept this buffer. this is used by SurfaceFlinger to
- // reject buffers which have the wrong size
- if (rejecter && rejecter->reject(mSlots[buf].mGraphicBuffer, item)) {
- releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR);
- glBindTexture(mTexTarget, mTexName);
- return NO_ERROR;
- }
-
- GLint error;
- while ((error = glGetError()) != GL_NO_ERROR) {
- ST_LOGW("updateTexImage: clearing GL error: %#04x", error);
- }
-
- EGLImageKHR image = mEglSlots[buf].mEglImage;
- glBindTexture(mTexTarget, mTexName);
- glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
-
- while ((error = glGetError()) != GL_NO_ERROR) {
- ST_LOGE("updateTexImage: error binding external texture image %p "
- "(slot %d): %#04x", image, buf, error);
- err = UNKNOWN_ERROR;
- }
-
- if (err == NO_ERROR) {
- err = syncForReleaseLocked(dpy);
- }
-
- if (err != NO_ERROR) {
- // Release the buffer we just acquired. It's not safe to
- // release the old buffer, so instead we just drop the new frame.
- releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR);
- return err;
- }
-
- ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)",
- mCurrentTexture,
- mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
- buf, mSlots[buf].mGraphicBuffer->handle);
-
- // release old buffer
- if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
- status_t status = releaseBufferLocked(mCurrentTexture, dpy,
- mEglSlots[mCurrentTexture].mEglFence);
- if (status != NO_ERROR && status != BufferQueue::STALE_BUFFER_SLOT) {
- ST_LOGE("updateTexImage: failed to release buffer: %s (%d)",
- strerror(-status), status);
- err = status;
- }
- }
-
- // Update the SurfaceTexture state.
- mCurrentTexture = buf;
- mCurrentTextureBuf = mSlots[buf].mGraphicBuffer;
- mCurrentCrop = item.mCrop;
- mCurrentTransform = item.mTransform;
- mCurrentScalingMode = item.mScalingMode;
- mCurrentTimestamp = item.mTimestamp;
- mCurrentFence = item.mFence;
- if (!skipSync) {
- // SurfaceFlinger needs to lazily perform GLES synchronization
- // only when it's actually going to use GLES for compositing.
- // Eventually SurfaceFlinger should have its own consumer class,
- // but for now we'll just hack it in to SurfaceTexture.
- // SurfaceFlinger is responsible for calling doGLFenceWait before
- // texturing from this SurfaceTexture.
- doGLFenceWaitLocked();
- }
- computeCurrentTransformMatrixLocked();
- } else {
- if (err < 0) {
- ST_LOGE("updateTexImage: acquire failed: %s (%d)",
- strerror(-err), err);
- return err;
- }
- // We always bind the texture even if we don't update its contents.
- glBindTexture(mTexTarget, mTexName);
- return OK;
- }
-
- return err;
+ return NO_ERROR;
}
void SurfaceTexture::setReleaseFence(int fenceFd) {
@@ -427,30 +471,8 @@
// The EGLImageKHR that was associated with the slot was destroyed when
// the SurfaceTexture was detached from the old context, so we need to
// recreate it here.
- EGLImageKHR image = createImage(dpy, mCurrentTextureBuf);
- if (image == EGL_NO_IMAGE_KHR) {
- return UNKNOWN_ERROR;
- }
-
- // Attach the current buffer to the GL texture.
- glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
-
- GLint error;
- status_t err = OK;
- while ((error = glGetError()) != GL_NO_ERROR) {
- ST_LOGE("attachToContext: error binding external texture image %p "
- "(slot %d): %#04x", image, mCurrentTexture, error);
- err = UNKNOWN_ERROR;
- }
-
- // We destroy the EGLImageKHR here because the current buffer may no
- // longer be associated with one of the buffer slots, so we have
- // nowhere to to store it. If the buffer is still associated with a
- // slot then another EGLImageKHR will be created next time that buffer
- // gets acquired in updateTexImage.
- eglDestroyImageKHR(dpy, image);
-
- if (err != OK) {
+ status_t err = bindUnslottedBufferLocked(dpy);
+ if (err != NO_ERROR) {
return err;
}
}
@@ -463,6 +485,38 @@
return OK;
}
+status_t SurfaceTexture::bindUnslottedBufferLocked(EGLDisplay dpy) {
+ ST_LOGV("bindUnslottedBuffer ct=%d ctb=%p",
+ mCurrentTexture, mCurrentTextureBuf.get());
+
+ // Create a temporary EGLImageKHR.
+ EGLImageKHR image = createImage(dpy, mCurrentTextureBuf);
+ if (image == EGL_NO_IMAGE_KHR) {
+ return UNKNOWN_ERROR;
+ }
+
+ // Attach the current buffer to the GL texture.
+ glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
+
+ GLint error;
+ status_t err = OK;
+ while ((error = glGetError()) != GL_NO_ERROR) {
+ ST_LOGE("bindUnslottedBuffer: error binding external texture image %p "
+ "(slot %d): %#04x", image, mCurrentTexture, error);
+ err = UNKNOWN_ERROR;
+ }
+
+ // We destroy the EGLImageKHR here because the current buffer may no
+ // longer be associated with one of the buffer slots, so we have
+ // nowhere to to store it. If the buffer is still associated with a
+ // slot then another EGLImageKHR will be created next time that buffer
+ // gets acquired in updateTexImage.
+ eglDestroyImageKHR(dpy, image);
+
+ return err;
+}
+
+
status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) {
ST_LOGV("syncForReleaseLocked");
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 329bbd5..f7870f4 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -17,6 +17,7 @@
GLExtensions.cpp \
MessageQueue.cpp \
SurfaceFlinger.cpp \
+ SurfaceFlingerConsumer.cpp \
SurfaceTextureLayer.cpp \
Transform.cpp \
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index b6aa005..1c5403f 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -73,7 +73,7 @@
HWComposer::HWCLayerInterface* layer) {
LayerBaseClient::onLayerDisplayed(hw, layer);
if (layer) {
- mSurfaceTexture->setReleaseFence(layer->getAndResetReleaseFenceFd());
+ mSurfaceFlingerConsumer->setReleaseFence(layer->getAndResetReleaseFenceFd());
}
}
@@ -81,20 +81,20 @@
{
LayerBaseClient::onFirstRef();
- // Creates a custom BufferQueue for SurfaceTexture to use
+ // Creates a custom BufferQueue for SurfaceFlingerConsumer to use
sp<BufferQueue> bq = new SurfaceTextureLayer();
- mSurfaceTexture = new SurfaceTexture(mTextureName, true,
+ mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(mTextureName, true,
GL_TEXTURE_EXTERNAL_OES, false, bq);
- mSurfaceTexture->setConsumerUsageBits(getEffectiveUsage(0));
- mSurfaceTexture->setFrameAvailableListener(this);
- mSurfaceTexture->setSynchronousMode(true);
+ mSurfaceFlingerConsumer->setConsumerUsageBits(getEffectiveUsage(0));
+ mSurfaceFlingerConsumer->setFrameAvailableListener(this);
+ mSurfaceFlingerConsumer->setSynchronousMode(true);
#ifdef TARGET_DISABLE_TRIPLE_BUFFERING
#warning "disabling triple buffering"
- mSurfaceTexture->setDefaultMaxBufferCount(2);
+ mSurfaceFlingerConsumer->setDefaultMaxBufferCount(2);
#else
- mSurfaceTexture->setDefaultMaxBufferCount(3);
+ mSurfaceFlingerConsumer->setDefaultMaxBufferCount(3);
#endif
const sp<const DisplayDevice> hw(mFlinger->getDefaultDisplayDevice());
@@ -115,12 +115,12 @@
// in the purgatory list
void Layer::onRemoved()
{
- mSurfaceTexture->abandon();
+ mSurfaceFlingerConsumer->abandon();
}
void Layer::setName(const String8& name) {
LayerBase::setName(name);
- mSurfaceTexture->setName(name);
+ mSurfaceFlingerConsumer->setName(name);
}
sp<ISurface> Layer::createSurface()
@@ -131,7 +131,7 @@
sp<ISurfaceTexture> res;
sp<const Layer> that( mOwner.promote() );
if (that != NULL) {
- res = that->mSurfaceTexture->getBufferQueue();
+ res = that->mSurfaceFlingerConsumer->getBufferQueue();
}
return res;
}
@@ -146,7 +146,7 @@
wp<IBinder> Layer::getSurfaceTextureBinder() const
{
- return mSurfaceTexture->getBufferQueue()->asBinder();
+ return mSurfaceFlingerConsumer->getBufferQueue()->asBinder();
}
status_t Layer::setBuffers( uint32_t w, uint32_t h,
@@ -177,15 +177,15 @@
mOpaqueLayer = (flags & ISurfaceComposerClient::eOpaque);
mCurrentOpacity = getOpacityForFormat(format);
- mSurfaceTexture->setDefaultBufferSize(w, h);
- mSurfaceTexture->setDefaultBufferFormat(format);
- mSurfaceTexture->setConsumerUsageBits(getEffectiveUsage(0));
+ mSurfaceFlingerConsumer->setDefaultBufferSize(w, h);
+ mSurfaceFlingerConsumer->setDefaultBufferFormat(format);
+ mSurfaceFlingerConsumer->setConsumerUsageBits(getEffectiveUsage(0));
return NO_ERROR;
}
Rect Layer::computeBufferCrop() const {
- // Start with the SurfaceTexture's buffer crop...
+ // Start with the SurfaceFlingerConsumer's buffer crop...
Rect crop;
if (!mCurrentCrop.isEmpty()) {
crop = mCurrentCrop;
@@ -202,7 +202,7 @@
if (!s.active.crop.isEmpty()) {
// Transform the window crop to match the buffer coordinate system,
// which means using the inverse of the current transform set on the
- // SurfaceTexture.
+ // SurfaceFlingerConsumer.
uint32_t invTransform = mCurrentTransform;
int winWidth = s.active.w;
int winHeight = s.active.h;
@@ -284,7 +284,7 @@
// acquire fence the first time a new buffer is acquired on EACH display.
if (layer.getCompositionType() == HWC_OVERLAY) {
- sp<Fence> fence = mSurfaceTexture->getCurrentFence();
+ sp<Fence> fence = mSurfaceFlingerConsumer->getCurrentFence();
if (fence.get()) {
fenceFd = fence->dup();
if (fenceFd == -1) {
@@ -327,7 +327,15 @@
return;
}
- status_t err = mSurfaceTexture->doGLFenceWait();
+ // Bind the current buffer to the GL texture.
+ status_t err = mSurfaceFlingerConsumer->bindTextureImage();
+ if (err != NO_ERROR) {
+ ALOGW("Layer::onDraw: bindTextureImage failed");
+ // keep going
+ }
+
+ // Wait for the buffer to be ready for us to draw into.
+ err = mSurfaceFlingerConsumer->doGLFenceWait();
if (err != OK) {
ALOGE("onDraw: failed waiting for fence: %d", err);
// Go ahead and draw the buffer anyway; no matter what we do the screen
@@ -342,8 +350,8 @@
// Query the texture matrix given our current filtering mode.
float textureMatrix[16];
- mSurfaceTexture->setFilteringEnabled(useFiltering);
- mSurfaceTexture->getTransformMatrix(textureMatrix);
+ mSurfaceFlingerConsumer->setFilteringEnabled(useFiltering);
+ mSurfaceFlingerConsumer->getTransformMatrix(textureMatrix);
// Set things up for texturing.
glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureName);
@@ -462,7 +470,7 @@
// record the new size, form this point on, when the client request
// a buffer, it'll get the new size.
- mSurfaceTexture->setDefaultBufferSize(
+ mSurfaceFlingerConsumer->setDefaultBufferSize(
temp.requested.w, temp.requested.h);
}
@@ -507,13 +515,10 @@
void Layer::onPostComposition() {
if (mFrameLatencyNeeded) {
- nsecs_t desiredPresentTime = mSurfaceTexture->getTimestamp();
+ nsecs_t desiredPresentTime = mSurfaceFlingerConsumer->getTimestamp();
mFrameTracker.setDesiredPresentTime(desiredPresentTime);
- sp<Fence> frameReadyFence = mSurfaceTexture->getCurrentFence();
- // XXX: Temporarily don't use the fence from the SurfaceTexture to
- // work around a driver bug.
- frameReadyFence.clear();
+ sp<Fence> frameReadyFence = mSurfaceFlingerConsumer->getCurrentFence();
if (frameReadyFence != NULL) {
mFrameTracker.setFrameReadyFence(frameReadyFence);
} else {
@@ -570,7 +575,7 @@
mFlinger->signalLayerUpdate();
}
- struct Reject : public SurfaceTexture::BufferRejecter {
+ struct Reject : public SurfaceFlingerConsumer::BufferRejecter {
Layer::State& front;
Layer::State& current;
bool& recomputeVisibleRegions;
@@ -655,14 +660,14 @@
Reject r(mDrawingState, currentState(), recomputeVisibleRegions);
- if (mSurfaceTexture->updateTexImage(&r, true) < NO_ERROR) {
+ if (mSurfaceFlingerConsumer->updateTexImage(&r) != NO_ERROR) {
// something happened!
recomputeVisibleRegions = true;
return outDirtyRegion;
}
// update the active buffer
- mActiveBuffer = mSurfaceTexture->getCurrentBuffer();
+ mActiveBuffer = mSurfaceFlingerConsumer->getCurrentBuffer();
if (mActiveBuffer == NULL) {
// this can only happen if the very first buffer was rejected.
return outDirtyRegion;
@@ -676,9 +681,9 @@
recomputeVisibleRegions = true;
}
- Rect crop(mSurfaceTexture->getCurrentCrop());
- const uint32_t transform(mSurfaceTexture->getCurrentTransform());
- const uint32_t scalingMode(mSurfaceTexture->getCurrentScalingMode());
+ Rect crop(mSurfaceFlingerConsumer->getCurrentCrop());
+ const uint32_t transform(mSurfaceFlingerConsumer->getCurrentTransform());
+ const uint32_t scalingMode(mSurfaceFlingerConsumer->getCurrentScalingMode());
if ((crop != mCurrentCrop) ||
(transform != mCurrentTransform) ||
(scalingMode != mCurrentScalingMode))
@@ -737,8 +742,8 @@
result.append(buffer);
- if (mSurfaceTexture != 0) {
- mSurfaceTexture->dump(result, " ", buffer, SIZE);
+ if (mSurfaceFlingerConsumer != 0) {
+ mSurfaceFlingerConsumer->dump(result, " ", buffer, SIZE);
}
}
@@ -780,7 +785,7 @@
orientation = 0;
}
}
- mSurfaceTexture->setTransformHint(orientation);
+ mSurfaceFlingerConsumer->setTransformHint(orientation);
}
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 5ea3eb4..2d4afc4 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -20,8 +20,6 @@
#include <stdint.h>
#include <sys/types.h>
-#include <gui/SurfaceTexture.h>
-
#include <utils/Timers.h>
#include <ui/GraphicBuffer.h>
@@ -34,6 +32,7 @@
#include <GLES/gl.h>
#include <GLES/glext.h>
+#include "SurfaceFlingerConsumer.h"
#include "FrameTracker.h"
#include "LayerBase.h"
#include "SurfaceTextureLayer.h"
@@ -49,7 +48,7 @@
// ---------------------------------------------------------------------------
class Layer : public LayerBaseClient,
- public SurfaceTexture::FrameAvailableListener
+ public SurfaceFlingerConsumer::FrameAvailableListener
{
public:
Layer(SurfaceFlinger* flinger, const sp<Client>& client);
@@ -92,7 +91,7 @@
// only for debugging
inline const sp<GraphicBuffer>& getActiveBuffer() const { return mActiveBuffer; }
- // Updates the transform hint in our SurfaceTexture to match
+ // Updates the transform hint in our SurfaceFlingerConsumer to match
// the current orientation of the display device.
virtual void updateTransformHint(const sp<const DisplayDevice>& hw) const;
@@ -110,13 +109,13 @@
Rect computeBufferCrop() const;
static bool getOpacityForFormat(uint32_t format);
- // Interface implementation for SurfaceTexture::FrameAvailableListener
+ // Interface implementation for SurfaceFlingerConsumer::FrameAvailableListener
virtual void onFrameAvailable();
// -----------------------------------------------------------------------
// constants
- sp<SurfaceTexture> mSurfaceTexture;
+ sp<SurfaceFlingerConsumer> mSurfaceFlingerConsumer;
GLuint mTextureName;
// thread-safe
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
new file mode 100644
index 0000000..dbe187b
--- /dev/null
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "SurfaceFlingerConsumer.h"
+
+#include <utils/Trace.h>
+#include <utils/Errors.h>
+
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter)
+{
+ ATRACE_CALL();
+ ALOGV("updateTexImage");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ ALOGE("updateTexImage: SurfaceTexture is abandoned!");
+ return NO_INIT;
+ }
+
+ // Make sure the EGL state is the same as in previous calls.
+ status_t err = checkAndUpdateEglStateLocked();
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ BufferQueue::BufferItem item;
+
+ // Acquire the next buffer.
+ // In asynchronous mode the list is guaranteed to be one buffer
+ // deep, while in synchronous mode we use the oldest buffer.
+ err = acquireBufferLocked(&item);
+ if (err != NO_ERROR) {
+ if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
+ // This variant of updateTexImage does not guarantee that the
+ // texture is bound, so no need to call glBindTexture.
+ err = NO_ERROR;
+ } else {
+ ALOGE("updateTexImage: acquire failed: %s (%d)",
+ strerror(-err), err);
+ }
+ return err;
+ }
+
+
+ // We call the rejecter here, in case the caller has a reason to
+ // not accept this buffer. This is used by SurfaceFlinger to
+ // reject buffers which have the wrong size
+ int buf = item.mBuf;
+ if (rejecter && rejecter->reject(mSlots[buf].mGraphicBuffer, item)) {
+ releaseBufferLocked(buf, EGL_NO_SYNC_KHR);
+ return NO_ERROR;
+ }
+
+ // Release the previous buffer.
+ err = releaseAndUpdateLocked(item);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ // Bind the new buffer to the GL texture.
+ // TODO: skip this on devices that support explicit sync
+ // (glEGLImageTargetTexture2DOES provides required implicit sync;
+ // without this we get wedged on older devices, but newer devices
+ // don't need it.)
+ return bindTextureImage();
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.h b/services/surfaceflinger/SurfaceFlingerConsumer.h
new file mode 100644
index 0000000..d91f27e
--- /dev/null
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SURFACEFLINGERCONSUMER_H
+#define ANDROID_SURFACEFLINGERCONSUMER_H
+
+#include <gui/SurfaceTexture.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+/*
+ * This is a thin wrapper around SurfaceTexture.
+ */
+class SurfaceFlingerConsumer : public SurfaceTexture {
+public:
+ SurfaceFlingerConsumer(GLuint tex, bool allowSynchronousMode = true,
+ GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true,
+ const sp<BufferQueue> &bufferQueue = 0)
+ : SurfaceTexture(tex, allowSynchronousMode, texTarget, useFenceSync,
+ bufferQueue)
+ {}
+
+ class BufferRejecter {
+ friend class SurfaceFlingerConsumer;
+ virtual bool reject(const sp<GraphicBuffer>& buf,
+ const BufferQueue::BufferItem& item) = 0;
+
+ protected:
+ virtual ~BufferRejecter() { }
+ };
+
+ // This version of updateTexImage() takes a functor that may be used to
+ // reject the newly acquired buffer. Unlike the SurfaceTexture version,
+ // this does not guarantee that the buffer has been bound to the GL
+ // texture.
+ status_t updateTexImage(BufferRejecter* rejecter);
+
+ // Pass-through to SurfaceTexture implementation.
+ status_t bindTextureImage() { return SurfaceTexture::bindTextureImage(); }
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_SURFACEFLINGERCONSUMER_H