summaryrefslogtreecommitdiffstats
path: root/libs/gui/SurfaceTexture.cpp
diff options
context:
space:
mode:
authorJamie Gennis <jgennis@google.com>2012-08-20 14:28:53 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2012-08-20 14:28:53 -0700
commitce7a6c0fc9d75b80da030d1790321e84475f956a (patch)
treef8d3a92d5c056a8e00d129381c4f74c51d4a082e /libs/gui/SurfaceTexture.cpp
parented059a8d754770c3cf28b78dba30f7a6ba475dbe (diff)
downloadframeworks_native-ce7a6c0fc9d75b80da030d1790321e84475f956a.zip
frameworks_native-ce7a6c0fc9d75b80da030d1790321e84475f956a.tar.gz
frameworks_native-ce7a6c0fc9d75b80da030d1790321e84475f956a.tar.bz2
Revert "SurfaceTexture: inherit from ConsumerBase"
This reverts commit ed059a8d754770c3cf28b78dba30f7a6ba475dbe Change-Id: I72542c2595771a40c2c88251e0d6eb54e305b99b
Diffstat (limited to 'libs/gui/SurfaceTexture.cpp')
-rw-r--r--libs/gui/SurfaceTexture.cpp284
1 files changed, 193 insertions, 91 deletions
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 451ccc2..6666081 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -26,8 +26,6 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
-#include <hardware/hardware.h>
-
#include <gui/IGraphicBufferAlloc.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
@@ -98,10 +96,14 @@ static float mtxRot270[16] = {
static void mtxMul(float out[16], const float a[16], const float b[16]);
+// Get an ID that's unique within this process.
+static int32_t createProcessUniqueId() {
+ static volatile int32_t globalCounter = 0;
+ return android_atomic_inc(&globalCounter);
+}
SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
GLenum texTarget, bool useFenceSync, const sp<BufferQueue> &bufferQueue) :
- ConsumerBase(bufferQueue == 0 ? new BufferQueue(allowSynchronousMode) : bufferQueue),
mCurrentTransform(0),
mCurrentTimestamp(0),
mFilteringEnabled(true),
@@ -114,15 +116,47 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
mTexTarget(texTarget),
mEglDisplay(EGL_NO_DISPLAY),
mEglContext(EGL_NO_CONTEXT),
+ mAbandoned(false),
mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
mAttached(true)
{
+ // Choose a name using the PID and a process-unique ID.
+ mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
ST_LOGV("SurfaceTexture");
+ if (bufferQueue == 0) {
+ ST_LOGV("Creating a new BufferQueue");
+ mBufferQueue = new BufferQueue(allowSynchronousMode);
+ }
+ else {
+ mBufferQueue = bufferQueue;
+ }
memcpy(mCurrentTransformMatrix, mtxIdentity,
sizeof(mCurrentTransformMatrix));
- mBufferQueue->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+ // Note that we can't create an sp<...>(this) in a ctor that will not keep a
+ // reference once the ctor ends, as that would cause the refcount of 'this'
+ // dropping to 0 at the end of the ctor. Since all we need is a wp<...>
+ // that's what we create.
+ wp<BufferQueue::ConsumerListener> listener;
+ sp<BufferQueue::ConsumerListener> proxy;
+ listener = static_cast<BufferQueue::ConsumerListener*>(this);
+ proxy = new BufferQueue::ProxyConsumerListener(listener);
+
+ status_t err = mBufferQueue->consumerConnect(proxy);
+ if (err != NO_ERROR) {
+ ST_LOGE("SurfaceTexture: error connecting to BufferQueue: %s (%d)",
+ strerror(-err), err);
+ } else {
+ mBufferQueue->setConsumerName(mName);
+ mBufferQueue->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+ }
+}
+
+SurfaceTexture::~SurfaceTexture() {
+ ST_LOGV("~SurfaceTexture");
+
+ abandon();
}
status_t SurfaceTexture::setBufferCountServer(int bufferCount) {
@@ -143,42 +177,6 @@ status_t SurfaceTexture::updateTexImage() {
return SurfaceTexture::updateTexImage(NULL);
}
-status_t SurfaceTexture::acquireBufferLocked(BufferQueue::BufferItem *item) {
- status_t err = ConsumerBase::acquireBufferLocked(item);
- if (err != NO_ERROR) {
- return err;
- }
-
- int slot = item->mBuf;
- if (item->mGraphicBuffer != NULL) {
- if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) {
- eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage);
- 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.
- 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, const sp<Fence>& fence) {
- status_t err = ConsumerBase::releaseBufferLocked(buf, mEglDisplay,
- eglFence, fence);
-
- mEglSlots[mCurrentTexture].mEglFence = EGL_NO_SYNC_KHR;
-
- return err;
-}
-
status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter) {
ATRACE_CALL();
ST_LOGV("updateTexImage");
@@ -219,65 +217,97 @@ status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter) {
// In asynchronous mode the list is guaranteed to be one buffer
// deep, while in synchronous mode we use the oldest buffer.
- err = acquireBufferLocked(&item);
+ err = mBufferQueue->acquireBuffer(&item);
if (err == NO_ERROR) {
int buf = item.mBuf;
+ // This buffer was newly allocated, so we need to clean up on our side
+ if (item.mGraphicBuffer != NULL) {
+ mEGLSlots[buf].mGraphicBuffer = 0;
+ if (mEGLSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) {
+ eglDestroyImageKHR(dpy, mEGLSlots[buf].mEglImage);
+ mEGLSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
+ }
+ mEGLSlots[buf].mGraphicBuffer = item.mGraphicBuffer;
+ }
// 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, item.mFence);
+ if (rejecter && rejecter->reject(mEGLSlots[buf].mGraphicBuffer, item)) {
+ mBufferQueue->releaseBuffer(buf, dpy, EGL_NO_SYNC_KHR, item.mFence);
glBindTexture(mTexTarget, mTexName);
return NO_ERROR;
}
- GLint error;
- while ((error = glGetError()) != GL_NO_ERROR) {
- ST_LOGW("updateTexImage: clearing GL error: %#04x", error);
+ // 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.
+ EGLImageKHR image = mEGLSlots[buf].mEglImage;
+ if (image == EGL_NO_IMAGE_KHR) {
+ if (mEGLSlots[buf].mGraphicBuffer == NULL) {
+ ST_LOGE("updateTexImage: buffer at slot %d is null", buf);
+ err = BAD_VALUE;
+ } else {
+ image = createImage(dpy, mEGLSlots[buf].mGraphicBuffer);
+ mEGLSlots[buf].mEglImage = image;
+ if (image == EGL_NO_IMAGE_KHR) {
+ // NOTE: if dpy was invalid, createImage() is guaranteed to
+ // fail. so we'd end up here.
+ err = UNKNOWN_ERROR;
+ }
+ }
}
- EGLImageKHR image = mEglSlots[buf].mEglImage;
- glBindTexture(mTexTarget, mTexName);
- glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
+ if (err == NO_ERROR) {
+ GLint error;
+ while ((error = glGetError()) != GL_NO_ERROR) {
+ ST_LOGW("updateTexImage: clearing GL error: %#04x", error);
+ }
- while ((error = glGetError()) != GL_NO_ERROR) {
- ST_LOGE("updateTexImage: error binding external texture image %p "
- "(slot %d): %#04x", image, buf, error);
- err = UNKNOWN_ERROR;
- }
+ glBindTexture(mTexTarget, mTexName);
+ glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
- if (err == NO_ERROR) {
- err = syncForReleaseLocked(dpy);
+ 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, item.mFence);
+ mBufferQueue->releaseBuffer(buf, dpy, EGL_NO_SYNC_KHR, item.mFence);
return err;
}
ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)",
mCurrentTexture,
mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
- buf, mSlots[buf].mGraphicBuffer->handle);
+ buf, item.mGraphicBuffer != NULL ? item.mGraphicBuffer->handle : 0);
// release old buffer
if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
- status_t status = releaseBufferLocked(mCurrentTexture, dpy,
- mEglSlots[mCurrentTexture].mEglFence,
- mSlots[mCurrentTexture].mFence);
- if (status != NO_ERROR && status != BufferQueue::STALE_BUFFER_SLOT) {
- ST_LOGE("updateTexImage: failed to release buffer: %s (%d)",
- strerror(-status), status);
+ status_t status = mBufferQueue->releaseBuffer(mCurrentTexture, dpy,
+ mEGLSlots[mCurrentTexture].mFence,
+ mEGLSlots[mCurrentTexture].mReleaseFence);
+ mEGLSlots[mCurrentTexture].mFence = EGL_NO_SYNC_KHR;
+ mEGLSlots[mCurrentTexture].mReleaseFence.clear();
+ if (status == BufferQueue::STALE_BUFFER_SLOT) {
+ freeBufferLocked(mCurrentTexture);
+ } else if (status != NO_ERROR) {
+ ST_LOGE("updateTexImage: released invalid buffer");
err = status;
}
}
// Update the SurfaceTexture state.
mCurrentTexture = buf;
- mCurrentTextureBuf = mSlots[buf].mGraphicBuffer;
+ mCurrentTextureBuf = mEGLSlots[buf].mGraphicBuffer;
mCurrentCrop = item.mCrop;
mCurrentTransform = item.mTransform;
mCurrentScalingMode = item.mScalingMode;
@@ -300,20 +330,20 @@ void SurfaceTexture::setReleaseFence(int fenceFd) {
sp<Fence> fence(new Fence(fenceFd));
if (fenceFd == -1 || mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT)
return;
- if (!mSlots[mCurrentTexture].mFence.get()) {
- mSlots[mCurrentTexture].mFence = fence;
+ if (!mEGLSlots[mCurrentTexture].mReleaseFence.get()) {
+ mEGLSlots[mCurrentTexture].mReleaseFence = fence;
} else {
sp<Fence> mergedFence = Fence::merge(
String8("SurfaceTexture merged release"),
- mSlots[mCurrentTexture].mFence, fence);
+ mEGLSlots[mCurrentTexture].mReleaseFence, fence);
if (!mergedFence.get()) {
ST_LOGE("failed to merge release fences");
// synchronization is broken, the best we can do is hope fences
// signal in order so the new fence will act like a union
- mSlots[mCurrentTexture].mFence = fence;
+ mEGLSlots[mCurrentTexture].mReleaseFence = fence;
return;
}
- mSlots[mCurrentTexture].mFence = mergedFence;
+ mEGLSlots[mCurrentTexture].mReleaseFence = mergedFence;
}
}
@@ -360,10 +390,10 @@ status_t SurfaceTexture::detachFromContext() {
// SurfaceTexture gets attached to a new OpenGL ES context (and thus gets a
// new EGLDisplay).
for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- EGLImageKHR img = mEglSlots[i].mEglImage;
+ EGLImageKHR img = mEGLSlots[i].mEglImage;
if (img != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(mEglDisplay, img);
- mEglSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
+ mEGLSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
}
}
@@ -451,7 +481,7 @@ status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) {
ST_LOGV("syncForReleaseLocked");
if (mUseFenceSync && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
- EGLSyncKHR fence = mEglSlots[mCurrentTexture].mEglFence;
+ EGLSyncKHR fence = mEGLSlots[mCurrentTexture].mFence;
if (fence != EGL_NO_SYNC_KHR) {
// There is already a fence for the current slot. We need to wait
// on that before replacing it with another fence to ensure that all
@@ -479,7 +509,7 @@ status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) {
return UNKNOWN_ERROR;
}
glFlush();
- mEglSlots[mCurrentTexture].mEglFence = fence;
+ mEGLSlots[mCurrentTexture].mFence = fence;
}
return OK;
@@ -577,12 +607,10 @@ void SurfaceTexture::computeCurrentTransformMatrix() {
// only need to shrink by a half a pixel.
shrinkAmount = 0.5;
break;
-
default:
// If we don't recognize the format, we must assume the
// worst case (that we care about), which is YUV420.
shrinkAmount = 1.0;
- break;
}
}
@@ -622,6 +650,13 @@ nsecs_t SurfaceTexture::getTimestamp() {
return mCurrentTimestamp;
}
+void SurfaceTexture::setFrameAvailableListener(
+ const sp<FrameAvailableListener>& listener) {
+ ST_LOGV("setFrameAvailableListener");
+ Mutex::Autolock lock(mMutex);
+ mFrameAvailableListener = listener;
+}
+
EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
const sp<GraphicBuffer>& graphicBuffer) {
EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
@@ -701,21 +736,35 @@ bool SurfaceTexture::isSynchronousMode() const {
void SurfaceTexture::freeBufferLocked(int slotIndex) {
ST_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
+ mEGLSlots[slotIndex].mGraphicBuffer = 0;
if (slotIndex == mCurrentTexture) {
mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
}
- EGLImageKHR img = mEglSlots[slotIndex].mEglImage;
+ EGLImageKHR img = mEGLSlots[slotIndex].mEglImage;
if (img != EGL_NO_IMAGE_KHR) {
ST_LOGV("destroying EGLImage dpy=%p img=%p", mEglDisplay, img);
eglDestroyImageKHR(mEglDisplay, img);
}
- mEglSlots[slotIndex].mEglImage = EGL_NO_IMAGE_KHR;
+ mEGLSlots[slotIndex].mEglImage = EGL_NO_IMAGE_KHR;
}
-void SurfaceTexture::abandonLocked() {
- ST_LOGV("abandonLocked");
- mCurrentTextureBuf.clear();
- ConsumerBase::abandonLocked();
+void SurfaceTexture::abandon() {
+ ST_LOGV("abandon");
+ Mutex::Autolock lock(mMutex);
+
+ if (!mAbandoned) {
+ mAbandoned = true;
+ mCurrentTextureBuf.clear();
+
+ // destroy all egl buffers
+ for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ freeBufferLocked(i);
+ }
+
+ // disconnect from the BufferQueue
+ mBufferQueue->consumerDisconnect();
+ mBufferQueue.clear();
+ }
}
void SurfaceTexture::setName(const String8& name) {
@@ -747,18 +796,71 @@ status_t SurfaceTexture::setSynchronousMode(bool enabled) {
return mBufferQueue->setSynchronousMode(enabled);
}
-void SurfaceTexture::dumpLocked(String8& result, const char* prefix,
- char* buffer, size_t size) const
+// Used for refactoring, should not be in final interface
+sp<BufferQueue> SurfaceTexture::getBufferQueue() const {
+ Mutex::Autolock lock(mMutex);
+ return mBufferQueue;
+}
+
+void SurfaceTexture::onFrameAvailable() {
+ ST_LOGV("onFrameAvailable");
+
+ sp<FrameAvailableListener> listener;
+ { // scope for the lock
+ Mutex::Autolock lock(mMutex);
+ listener = mFrameAvailableListener;
+ }
+
+ if (listener != NULL) {
+ ST_LOGV("actually calling onFrameAvailable");
+ listener->onFrameAvailable();
+ }
+}
+
+void SurfaceTexture::onBuffersReleased() {
+ ST_LOGV("onBuffersReleased");
+
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ // Nothing to do if we're already abandoned.
+ return;
+ }
+
+ uint32_t mask = 0;
+ mBufferQueue->getReleasedBuffers(&mask);
+ for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ if (mask & (1 << i)) {
+ freeBufferLocked(i);
+ }
+ }
+}
+
+void SurfaceTexture::dump(String8& result) const
+{
+ char buffer[1024];
+ dump(result, "", buffer, 1024);
+}
+
+void SurfaceTexture::dump(String8& result, const char* prefix,
+ char* buffer, size_t SIZE) const
{
- snprintf(buffer, size,
- "%smTexName=%d mCurrentTexture=%d\n"
- "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n",
- prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left,
- mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
- mCurrentTransform);
+ Mutex::Autolock _l(mMutex);
+ snprintf(buffer, SIZE, "%smTexName=%d, mAbandoned=%d\n", prefix, mTexName,
+ int(mAbandoned));
result.append(buffer);
- ConsumerBase::dumpLocked(result, prefix, buffer, size);
+ snprintf(buffer, SIZE,
+ "%snext : {crop=[%d,%d,%d,%d], transform=0x%02x, current=%d}\n",
+ prefix, mCurrentCrop.left,
+ mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
+ mCurrentTransform, mCurrentTexture
+ );
+ result.append(buffer);
+
+ if (!mAbandoned) {
+ mBufferQueue->dump(result, prefix, buffer, SIZE);
+ }
}
static void mtxMul(float out[16], const float a[16], const float b[16]) {