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
This commit is contained in:
Andy McFadden 2012-12-04 16:51:15 -08:00
parent 0e1e53e376
commit bf974abe92
9 changed files with 433 additions and 217 deletions

View File

@ -182,8 +182,9 @@ public:
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.

View File

@ -74,14 +74,13 @@ public:
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 @@ public:
// 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 @@ protected:
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 @@ private:
// 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 @@ private:
// 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 @@ private:
// 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,

View File

@ -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

View File

@ -154,7 +154,56 @@ status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h)
}
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 @@ status_t SurfaceTexture::acquireBufferLocked(BufferQueue::BufferItem *item) {
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 @@ status_t SurfaceTexture::attachToContext(GLuint tex) {
// 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 @@ status_t SurfaceTexture::attachToContext(GLuint tex) {
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");

View File

@ -17,6 +17,7 @@ LOCAL_SRC_FILES:= \
GLExtensions.cpp \
MessageQueue.cpp \
SurfaceFlinger.cpp \
SurfaceFlingerConsumer.cpp \
SurfaceTextureLayer.cpp \
Transform.cpp \

View File

@ -73,7 +73,7 @@ void Layer::onLayerDisplayed(const sp<const DisplayDevice>& hw,
HWComposer::HWCLayerInterface* layer) {
LayerBaseClient::onLayerDisplayed(hw, layer);
if (layer) {
mSurfaceTexture->setReleaseFence(layer->getAndResetReleaseFenceFd());
mSurfaceFlingerConsumer->setReleaseFence(layer->getAndResetReleaseFenceFd());
}
}
@ -81,20 +81,20 @@ void Layer::onFirstRef()
{
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 @@ void Layer::onFrameAvailable() {
// 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<ISurface> Layer::createSurface()
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 @@ sp<ISurface> Layer::createSurface()
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 @@ status_t Layer::setBuffers( uint32_t w, uint32_t h,
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 @@ Rect Layer::computeBufferCrop() const {
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 @@ void Layer::setAcquireFence(const sp<const DisplayDevice>& hw,
// 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 @@ void Layer::onDraw(const sp<const DisplayDevice>& hw, const Region& clip) const
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 @@ void Layer::onDraw(const sp<const DisplayDevice>& hw, const Region& clip) const
// 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 @@ uint32_t Layer::doTransaction(uint32_t flags)
// 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 @@ bool Layer::onPreComposition() {
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 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions)
mFlinger->signalLayerUpdate();
}
struct Reject : public SurfaceTexture::BufferRejecter {
struct Reject : public SurfaceFlingerConsumer::BufferRejecter {
Layer::State& front;
Layer::State& current;
bool& recomputeVisibleRegions;
@ -655,14 +660,14 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions)
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 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions)
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 @@ void Layer::dump(String8& result, char* buffer, size_t SIZE) const
result.append(buffer);
if (mSurfaceTexture != 0) {
mSurfaceTexture->dump(result, " ", buffer, SIZE);
if (mSurfaceFlingerConsumer != 0) {
mSurfaceFlingerConsumer->dump(result, " ", buffer, SIZE);
}
}
@ -780,7 +785,7 @@ void Layer::updateTransformHint(const sp<const DisplayDevice>& hw) const {
orientation = 0;
}
}
mSurfaceTexture->setTransformHint(orientation);
mSurfaceFlingerConsumer->setTransformHint(orientation);
}
// ---------------------------------------------------------------------------

View File

@ -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 GLExtensions;
// ---------------------------------------------------------------------------
class Layer : public LayerBaseClient,
public SurfaceTexture::FrameAvailableListener
public SurfaceFlingerConsumer::FrameAvailableListener
{
public:
Layer(SurfaceFlinger* flinger, const sp<Client>& client);
@ -92,7 +91,7 @@ public:
// 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 @@ private:
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

View File

@ -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

View File

@ -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