From 2adaf04fab35cf47c824d74d901b54094e01ccd3 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Tue, 18 Dec 2012 09:49:45 -0800 Subject: Rename ISurfaceTexture and SurfaceTexture The C++ class names don't match what the classes do, so rename ISurfaceTexture to IGraphicBufferProducer, and SurfaceTexture to GLConsumer. Bug 7736700 Change-Id: Ia03e468888025b5cae3c0ee1995434515dbea387 --- libs/gui/Android.mk | 4 +- libs/gui/BufferQueue.cpp | 20 +- libs/gui/ConsumerBase.cpp | 4 +- libs/gui/GLConsumer.cpp | 961 +++++++++++++++++++++++++++ libs/gui/IGraphicBufferProducer.cpp | 350 ++++++++++ libs/gui/ISurface.cpp | 6 +- libs/gui/ISurfaceComposer.cpp | 12 +- libs/gui/ISurfaceTexture.cpp | 350 ---------- libs/gui/LayerState.cpp | 4 +- libs/gui/Surface.cpp | 30 +- libs/gui/SurfaceComposerClient.cpp | 11 +- libs/gui/SurfaceTexture.cpp | 961 --------------------------- libs/gui/SurfaceTextureClient.cpp | 28 +- libs/gui/tests/BufferQueue_test.cpp | 8 +- libs/gui/tests/SurfaceTextureClient_test.cpp | 14 +- libs/gui/tests/SurfaceTexture_test.cpp | 38 +- 16 files changed, 1401 insertions(+), 1400 deletions(-) create mode 100644 libs/gui/GLConsumer.cpp create mode 100644 libs/gui/IGraphicBufferProducer.cpp delete mode 100644 libs/gui/ISurfaceTexture.cpp delete mode 100644 libs/gui/SurfaceTexture.cpp (limited to 'libs/gui') diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk index 04444e9371..b1aeb32889 100644 --- a/libs/gui/Android.mk +++ b/libs/gui/Android.mk @@ -9,23 +9,23 @@ LOCAL_SRC_FILES:= \ CpuConsumer.cpp \ DisplayEventReceiver.cpp \ DummyConsumer.cpp \ + GLConsumer.cpp \ GraphicBufferAlloc.cpp \ GuiConfig.cpp \ IDisplayEventConnection.cpp \ IGraphicBufferAlloc.cpp \ + IGraphicBufferProducer.cpp \ ISensorEventConnection.cpp \ ISensorServer.cpp \ ISurface.cpp \ ISurfaceComposer.cpp \ ISurfaceComposerClient.cpp \ - ISurfaceTexture.cpp \ LayerState.cpp \ Sensor.cpp \ SensorEventQueue.cpp \ SensorManager.cpp \ Surface.cpp \ SurfaceComposerClient.cpp \ - SurfaceTexture.cpp \ SurfaceTextureClient.cpp \ LOCAL_SHARED_LIBRARIES := \ diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp index 609e7d2c9e..4be655b3f8 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -146,7 +146,7 @@ status_t BufferQueue::setBufferCount(int bufferCount) { Mutex::Autolock lock(mMutex); if (mAbandoned) { - ST_LOGE("setBufferCount: SurfaceTexture has been abandoned!"); + ST_LOGE("setBufferCount: BufferQueue has been abandoned!"); return NO_INIT; } if (bufferCount > NUM_BUFFER_SLOTS) { @@ -200,7 +200,7 @@ int BufferQueue::query(int what, int* outValue) Mutex::Autolock lock(mMutex); if (mAbandoned) { - ST_LOGE("query: SurfaceTexture has been abandoned!"); + ST_LOGE("query: BufferQueue has been abandoned!"); return NO_INIT; } @@ -233,7 +233,7 @@ status_t BufferQueue::requestBuffer(int slot, sp* buf) { ST_LOGV("requestBuffer: slot=%d", slot); Mutex::Autolock lock(mMutex); if (mAbandoned) { - ST_LOGE("requestBuffer: SurfaceTexture has been abandoned!"); + ST_LOGE("requestBuffer: BufferQueue has been abandoned!"); return NO_INIT; } int maxBufferCount = getMaxBufferCountLocked(); @@ -282,7 +282,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp& outFence, bool tryAgain = true; while (tryAgain) { if (mAbandoned) { - ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!"); + ST_LOGE("dequeueBuffer: BufferQueue has been abandoned!"); return NO_INIT; } @@ -294,7 +294,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp& outFence, assert(mSlots[i].mBufferState == BufferSlot::FREE); if (mSlots[i].mGraphicBuffer != NULL) { freeBufferLocked(i); - returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS; + returnFlags |= IGraphicBufferProducer::RELEASE_ALL_BUFFERS; } } @@ -390,7 +390,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp& outFence, mSlots[buf].mFence.clear(); mSlots[buf].mEglDisplay = EGL_NO_DISPLAY; - returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION; + returnFlags |= IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION; } dpy = mSlots[buf].mEglDisplay; @@ -400,7 +400,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp& outFence, mSlots[buf].mFence.clear(); } // end lock scope - if (returnFlags & ISurfaceTexture::BUFFER_NEEDS_REALLOCATION) { + if (returnFlags & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) { status_t error; sp graphicBuffer( mGraphicBufferAlloc->createGraphicBuffer( @@ -415,7 +415,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp& outFence, Mutex::Autolock lock(mMutex); if (mAbandoned) { - ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!"); + ST_LOGE("dequeueBuffer: BufferQueue has been abandoned!"); return NO_INIT; } @@ -449,7 +449,7 @@ status_t BufferQueue::setSynchronousMode(bool enabled) { Mutex::Autolock lock(mMutex); if (mAbandoned) { - ST_LOGE("setSynchronousMode: SurfaceTexture has been abandoned!"); + ST_LOGE("setSynchronousMode: BufferQueue has been abandoned!"); return NO_INIT; } @@ -498,7 +498,7 @@ status_t BufferQueue::queueBuffer(int buf, { // scope for the lock Mutex::Autolock lock(mMutex); if (mAbandoned) { - ST_LOGE("queueBuffer: SurfaceTexture has been abandoned!"); + ST_LOGE("queueBuffer: BufferQueue has been abandoned!"); return NO_INIT; } int maxBufferCount = getMaxBufferCountLocked(); diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp index 2e7c42414c..8f391aa495 100644 --- a/libs/gui/ConsumerBase.cpp +++ b/libs/gui/ConsumerBase.cpp @@ -69,7 +69,7 @@ ConsumerBase::ConsumerBase(const sp& bufferQueue) : status_t err = mBufferQueue->consumerConnect(proxy); if (err != NO_ERROR) { - CB_LOGE("SurfaceTexture: error connecting to BufferQueue: %s (%d)", + CB_LOGE("ConsumerBase: error connecting to BufferQueue: %s (%d)", strerror(-err), err); } else { mBufferQueue->setConsumerName(mName); @@ -233,4 +233,4 @@ status_t ConsumerBase::releaseBufferLocked(int slot, EGLDisplay display, return err; } -} // namespace android \ No newline at end of file +} // namespace android diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp new file mode 100644 index 0000000000..ba23f9edf8 --- /dev/null +++ b/libs/gui/GLConsumer.cpp @@ -0,0 +1,961 @@ +/* + * Copyright (C) 2010 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 LOG_TAG "GLConsumer" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +//#define LOG_NDEBUG 0 + +#define GL_GLEXT_PROTOTYPES +#define EGL_EGLEXT_PROTOTYPES + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace android { + +// This compile option makes GLConsumer use the +// EGL_ANDROID_native_fence_sync extension to create Android native fences to +// signal when all GLES reads for a given buffer have completed. It is not +// compatible with using the EGL_KHR_fence_sync extension for the same +// purpose. +#ifdef USE_NATIVE_FENCE_SYNC +#ifdef USE_FENCE_SYNC +#error "USE_NATIVE_FENCE_SYNC and USE_FENCE_SYNC are incompatible" +#endif +const bool GLConsumer::sUseNativeFenceSync = true; +#else +const bool GLConsumer::sUseNativeFenceSync = false; +#endif + +// This compile option makes GLConsumer use the EGL_ANDROID_sync_wait +// extension to insert server-side waits into the GLES command stream. This +// feature requires the EGL_ANDROID_native_fence_sync and +// EGL_ANDROID_wait_sync extensions. +#ifdef USE_WAIT_SYNC +static const bool useWaitSync = true; +#else +static const bool useWaitSync = false; +#endif + +// Macros for including the GLConsumer name in log messages +#define ST_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__) +#define ST_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__) +#define ST_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__) +#define ST_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__) +#define ST_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__) + +// Transform matrices +static float mtxIdentity[16] = { + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1, +}; +static float mtxFlipH[16] = { + -1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 1, 0, 0, 1, +}; +static float mtxFlipV[16] = { + 1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, 1, 0, + 0, 1, 0, 1, +}; +static float mtxRot90[16] = { + 0, 1, 0, 0, + -1, 0, 0, 0, + 0, 0, 1, 0, + 1, 0, 0, 1, +}; +static float mtxRot180[16] = { + -1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, 1, 0, + 1, 1, 0, 1, +}; +static float mtxRot270[16] = { + 0, -1, 0, 0, + 1, 0, 0, 0, + 0, 0, 1, 0, + 0, 1, 0, 1, +}; + +static void mtxMul(float out[16], const float a[16], const float b[16]); + + +GLConsumer::GLConsumer(GLuint tex, bool allowSynchronousMode, + GLenum texTarget, bool useFenceSync, const sp &bufferQueue) : + ConsumerBase(bufferQueue == 0 ? new BufferQueue(allowSynchronousMode) : bufferQueue), + mCurrentTransform(0), + mCurrentTimestamp(0), + mFilteringEnabled(true), + mTexName(tex), +#ifdef USE_FENCE_SYNC + mUseFenceSync(useFenceSync), +#else + mUseFenceSync(false), +#endif + mTexTarget(texTarget), + mEglDisplay(EGL_NO_DISPLAY), + mEglContext(EGL_NO_CONTEXT), + mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), + mAttached(true) +{ + ST_LOGV("GLConsumer"); + + memcpy(mCurrentTransformMatrix, mtxIdentity, + sizeof(mCurrentTransformMatrix)); + + mBufferQueue->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); +} + +status_t GLConsumer::setDefaultMaxBufferCount(int bufferCount) { + Mutex::Autolock lock(mMutex); + return mBufferQueue->setDefaultMaxBufferCount(bufferCount); +} + + +status_t GLConsumer::setDefaultBufferSize(uint32_t w, uint32_t h) +{ + Mutex::Autolock lock(mMutex); + mDefaultWidth = w; + mDefaultHeight = h; + return mBufferQueue->setDefaultBufferSize(w, h); +} + +status_t GLConsumer::updateTexImage() { + ATRACE_CALL(); + ST_LOGV("updateTexImage"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGE("updateTexImage: GLConsumer 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, and wait until it's ready. + return bindTextureImageLocked(); +} + +status_t GLConsumer::acquireBufferLocked(BufferQueue::BufferItem *item) { + status_t err = ConsumerBase::acquireBufferLocked(item); + if (err != NO_ERROR) { + return err; + } + + 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) { + if (!eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage)) { + ST_LOGW("acquireBufferLocked: eglDestroyImageKHR failed for slot=%d", + slot); + // keep going + } + mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR; + } + } + + return NO_ERROR; +} + +status_t GLConsumer::releaseBufferLocked(int buf, EGLDisplay display, + EGLSyncKHR eglFence) { + status_t err = ConsumerBase::releaseBufferLocked(buf, display, eglFence); + + mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR; + + return err; +} + +status_t GLConsumer::releaseAndUpdateLocked(const BufferQueue::BufferItem& item) +{ + status_t err = NO_ERROR; + + if (!mAttached) { + ST_LOGE("releaseAndUpdate: GLConsumer 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 GLConsumer 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 GLConsumer::bindTextureImageLocked() { + 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; + } + status_t err = bindUnslottedBufferLocked(mEglDisplay); + if (err != NO_ERROR) { + return err; + } + } 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; + } + } + + // Wait for the new buffer to be ready. + return doGLFenceWaitLocked(); + +} + +status_t GLConsumer::checkAndUpdateEglStateLocked() { + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if ((mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) || + dpy == EGL_NO_DISPLAY) { + ST_LOGE("checkAndUpdateEglState: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if ((mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) || + ctx == EGL_NO_CONTEXT) { + ST_LOGE("checkAndUpdateEglState: invalid current EGLContext"); + return INVALID_OPERATION; + } + + mEglDisplay = dpy; + mEglContext = ctx; + return NO_ERROR; +} + +void GLConsumer::setReleaseFence(int fenceFd) { + sp fence(new Fence(fenceFd)); + if (fenceFd == -1 || mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) + return; + status_t err = addReleaseFence(mCurrentTexture, fence); + if (err != OK) { + ST_LOGE("setReleaseFence: failed to add the fence: %s (%d)", + strerror(-err), err); + } +} + +status_t GLConsumer::detachFromContext() { + ATRACE_CALL(); + ST_LOGV("detachFromContext"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGE("detachFromContext: abandoned GLConsumer"); + return NO_INIT; + } + + if (!mAttached) { + ST_LOGE("detachFromContext: GLConsumer is not attached to a " + "context"); + return INVALID_OPERATION; + } + + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) { + ST_LOGE("detachFromContext: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) { + ST_LOGE("detachFromContext: invalid current EGLContext"); + return INVALID_OPERATION; + } + + if (dpy != EGL_NO_DISPLAY && ctx != EGL_NO_CONTEXT) { + status_t err = syncForReleaseLocked(dpy); + if (err != OK) { + return err; + } + + glDeleteTextures(1, &mTexName); + } + + // Because we're giving up the EGLDisplay we need to free all the EGLImages + // that are associated with it. They'll be recreated when the + // GLConsumer 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; + if (img != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(mEglDisplay, img); + mEglSlots[i].mEglImage = EGL_NO_IMAGE_KHR; + } + } + + mEglDisplay = EGL_NO_DISPLAY; + mEglContext = EGL_NO_CONTEXT; + mAttached = false; + + return OK; +} + +status_t GLConsumer::attachToContext(GLuint tex) { + ATRACE_CALL(); + ST_LOGV("attachToContext"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGE("attachToContext: abandoned GLConsumer"); + return NO_INIT; + } + + if (mAttached) { + ST_LOGE("attachToContext: GLConsumer is already attached to a " + "context"); + return INVALID_OPERATION; + } + + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (dpy == EGL_NO_DISPLAY) { + ST_LOGE("attachToContext: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if (ctx == EGL_NO_CONTEXT) { + ST_LOGE("attachToContext: invalid current EGLContext"); + return INVALID_OPERATION; + } + + // We need to bind the texture regardless of whether there's a current + // buffer. + glBindTexture(mTexTarget, tex); + + if (mCurrentTextureBuf != NULL) { + // The EGLImageKHR that was associated with the slot was destroyed when + // the GLConsumer was detached from the old context, so we need to + // recreate it here. + status_t err = bindUnslottedBufferLocked(dpy); + if (err != NO_ERROR) { + return err; + } + } + + mEglDisplay = dpy; + mEglContext = ctx; + mTexName = tex; + mAttached = true; + + return OK; +} + +status_t GLConsumer::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 GLConsumer::syncForReleaseLocked(EGLDisplay dpy) { + ST_LOGV("syncForReleaseLocked"); + + if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + if (sUseNativeFenceSync) { + EGLSyncKHR sync = eglCreateSyncKHR(dpy, + EGL_SYNC_NATIVE_FENCE_ANDROID, NULL); + if (sync == EGL_NO_SYNC_KHR) { + ST_LOGE("syncForReleaseLocked: error creating EGL fence: %#x", + eglGetError()); + return UNKNOWN_ERROR; + } + glFlush(); + int fenceFd = eglDupNativeFenceFDANDROID(dpy, sync); + eglDestroySyncKHR(dpy, sync); + if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + ST_LOGE("syncForReleaseLocked: error dup'ing native fence " + "fd: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + sp fence(new Fence(fenceFd)); + status_t err = addReleaseFenceLocked(mCurrentTexture, fence); + if (err != OK) { + ST_LOGE("syncForReleaseLocked: error adding release fence: " + "%s (%d)", strerror(-err), err); + return err; + } + } else if (mUseFenceSync) { + EGLSyncKHR fence = mEglSlots[mCurrentTexture].mEglFence; + 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 outstanding buffer accesses have completed + // before the producer accesses it. + EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); + if (result == EGL_FALSE) { + ST_LOGE("syncForReleaseLocked: error waiting for previous " + "fence: %#x", eglGetError()); + return UNKNOWN_ERROR; + } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { + ST_LOGE("syncForReleaseLocked: timeout waiting for previous " + "fence"); + return TIMED_OUT; + } + eglDestroySyncKHR(dpy, fence); + } + + // Create a fence for the outstanding accesses in the current + // OpenGL ES context. + fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL); + if (fence == EGL_NO_SYNC_KHR) { + ST_LOGE("syncForReleaseLocked: error creating fence: %#x", + eglGetError()); + return UNKNOWN_ERROR; + } + glFlush(); + mEglSlots[mCurrentTexture].mEglFence = fence; + } + } + + return OK; +} + +bool GLConsumer::isExternalFormat(uint32_t format) +{ + switch (format) { + // supported YUV formats + case HAL_PIXEL_FORMAT_YV12: + // Legacy/deprecated YUV formats + case HAL_PIXEL_FORMAT_YCbCr_422_SP: + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + case HAL_PIXEL_FORMAT_YCbCr_422_I: + return true; + } + + // Any OEM format needs to be considered + if (format>=0x100 && format<=0x1FF) + return true; + + return false; +} + +GLenum GLConsumer::getCurrentTextureTarget() const { + return mTexTarget; +} + +void GLConsumer::getTransformMatrix(float mtx[16]) { + Mutex::Autolock lock(mMutex); + memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix)); +} + +void GLConsumer::setFilteringEnabled(bool enabled) { + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + ST_LOGE("setFilteringEnabled: GLConsumer is abandoned!"); + return; + } + bool needsRecompute = mFilteringEnabled != enabled; + mFilteringEnabled = enabled; + + if (needsRecompute && mCurrentTextureBuf==NULL) { + ST_LOGD("setFilteringEnabled called with mCurrentTextureBuf == NULL"); + } + + if (needsRecompute && mCurrentTextureBuf != NULL) { + computeCurrentTransformMatrixLocked(); + } +} + +void GLConsumer::computeCurrentTransformMatrixLocked() { + ST_LOGV("computeCurrentTransformMatrixLocked"); + + float xform[16]; + for (int i = 0; i < 16; i++) { + xform[i] = mtxIdentity[i]; + } + if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) { + float result[16]; + mtxMul(result, xform, mtxFlipH); + for (int i = 0; i < 16; i++) { + xform[i] = result[i]; + } + } + if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) { + float result[16]; + mtxMul(result, xform, mtxFlipV); + for (int i = 0; i < 16; i++) { + xform[i] = result[i]; + } + } + if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) { + float result[16]; + mtxMul(result, xform, mtxRot90); + for (int i = 0; i < 16; i++) { + xform[i] = result[i]; + } + } + + sp& buf(mCurrentTextureBuf); + + if (buf == NULL) { + ST_LOGD("computeCurrentTransformMatrixLocked: mCurrentTextureBuf is NULL"); + } + + Rect cropRect = mCurrentCrop; + float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f; + float bufferWidth = buf->getWidth(); + float bufferHeight = buf->getHeight(); + if (!cropRect.isEmpty()) { + float shrinkAmount = 0.0f; + if (mFilteringEnabled) { + // In order to prevent bilinear sampling beyond the edge of the + // crop rectangle we may need to shrink it by 2 texels in each + // dimension. Normally this would just need to take 1/2 a texel + // off each end, but because the chroma channels of YUV420 images + // are subsampled we may need to shrink the crop region by a whole + // texel on each side. + switch (buf->getPixelFormat()) { + case PIXEL_FORMAT_RGBA_8888: + case PIXEL_FORMAT_RGBX_8888: + case PIXEL_FORMAT_RGB_888: + case PIXEL_FORMAT_RGB_565: + case PIXEL_FORMAT_BGRA_8888: + case PIXEL_FORMAT_RGBA_5551: + case PIXEL_FORMAT_RGBA_4444: + // We know there's no subsampling of any channels, so we + // 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; + } + } + + // Only shrink the dimensions that are not the size of the buffer. + if (cropRect.width() < bufferWidth) { + tx = (float(cropRect.left) + shrinkAmount) / bufferWidth; + sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / + bufferWidth; + } + if (cropRect.height() < bufferHeight) { + ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / + bufferHeight; + sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / + bufferHeight; + } + } + float crop[16] = { + sx, 0, 0, 0, + 0, sy, 0, 0, + 0, 0, 1, 0, + tx, ty, 0, 1, + }; + + float mtxBeforeFlipV[16]; + mtxMul(mtxBeforeFlipV, crop, xform); + + // SurfaceFlinger expects the top of its window textures to be at a Y + // coordinate of 0, so GLConsumer must behave the same way. We don't + // want to expose this to applications, however, so we must add an + // additional vertical flip to the transform after all the other transforms. + mtxMul(mCurrentTransformMatrix, mtxFlipV, mtxBeforeFlipV); +} + +nsecs_t GLConsumer::getTimestamp() { + ST_LOGV("getTimestamp"); + Mutex::Autolock lock(mMutex); + return mCurrentTimestamp; +} + +EGLImageKHR GLConsumer::createImage(EGLDisplay dpy, + const sp& graphicBuffer) { + EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer(); + EGLint attrs[] = { + EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, + EGL_NONE, + }; + EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, + EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs); + if (image == EGL_NO_IMAGE_KHR) { + EGLint error = eglGetError(); + ST_LOGE("error creating EGLImage: %#x", error); + } + return image; +} + +sp GLConsumer::getCurrentBuffer() const { + Mutex::Autolock lock(mMutex); + return mCurrentTextureBuf; +} + +Rect GLConsumer::getCurrentCrop() const { + Mutex::Autolock lock(mMutex); + + Rect outCrop = mCurrentCrop; + if (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) { + int32_t newWidth = mCurrentCrop.width(); + int32_t newHeight = mCurrentCrop.height(); + + if (newWidth * mDefaultHeight > newHeight * mDefaultWidth) { + newWidth = newHeight * mDefaultWidth / mDefaultHeight; + ST_LOGV("too wide: newWidth = %d", newWidth); + } else if (newWidth * mDefaultHeight < newHeight * mDefaultWidth) { + newHeight = newWidth * mDefaultHeight / mDefaultWidth; + ST_LOGV("too tall: newHeight = %d", newHeight); + } + + // The crop is too wide + if (newWidth < mCurrentCrop.width()) { + int32_t dw = (newWidth - mCurrentCrop.width())/2; + outCrop.left -=dw; + outCrop.right += dw; + // The crop is too tall + } else if (newHeight < mCurrentCrop.height()) { + int32_t dh = (newHeight - mCurrentCrop.height())/2; + outCrop.top -= dh; + outCrop.bottom += dh; + } + + ST_LOGV("getCurrentCrop final crop [%d,%d,%d,%d]", + outCrop.left, outCrop.top, + outCrop.right,outCrop.bottom); + } + + return outCrop; +} + +uint32_t GLConsumer::getCurrentTransform() const { + Mutex::Autolock lock(mMutex); + return mCurrentTransform; +} + +uint32_t GLConsumer::getCurrentScalingMode() const { + Mutex::Autolock lock(mMutex); + return mCurrentScalingMode; +} + +sp GLConsumer::getCurrentFence() const { + Mutex::Autolock lock(mMutex); + return mCurrentFence; +} + +status_t GLConsumer::doGLFenceWait() const { + Mutex::Autolock lock(mMutex); + return doGLFenceWaitLocked(); +} + +status_t GLConsumer::doGLFenceWaitLocked() const { + + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (mEglDisplay != dpy || mEglDisplay == EGL_NO_DISPLAY) { + ST_LOGE("doGLFenceWait: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if (mEglContext != ctx || mEglContext == EGL_NO_CONTEXT) { + ST_LOGE("doGLFenceWait: invalid current EGLContext"); + return INVALID_OPERATION; + } + + if (mCurrentFence != NULL) { + if (useWaitSync) { + // Create an EGLSyncKHR from the current fence. + int fenceFd = mCurrentFence->dup(); + if (fenceFd == -1) { + ST_LOGE("doGLFenceWait: error dup'ing fence fd: %d", errno); + return -errno; + } + EGLint attribs[] = { + EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, + EGL_NONE + }; + EGLSyncKHR sync = eglCreateSyncKHR(dpy, + EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); + if (sync == EGL_NO_SYNC_KHR) { + close(fenceFd); + ST_LOGE("doGLFenceWait: error creating EGL fence: %#x", + eglGetError()); + return UNKNOWN_ERROR; + } + + // XXX: The spec draft is inconsistent as to whether this should + // return an EGLint or void. Ignore the return value for now, as + // it's not strictly needed. + eglWaitSyncANDROID(dpy, sync, 0); + EGLint eglErr = eglGetError(); + eglDestroySyncKHR(dpy, sync); + if (eglErr != EGL_SUCCESS) { + ST_LOGE("doGLFenceWait: error waiting for EGL fence: %#x", + eglErr); + return UNKNOWN_ERROR; + } + } else { + status_t err = mCurrentFence->waitForever(1000, + "GLConsumer::doGLFenceWaitLocked"); + if (err != NO_ERROR) { + ST_LOGE("doGLFenceWait: error waiting for fence: %d", err); + return err; + } + } + } + + return NO_ERROR; +} + +bool GLConsumer::isSynchronousMode() const { + Mutex::Autolock lock(mMutex); + return mBufferQueue->isSynchronousMode(); +} + +void GLConsumer::freeBufferLocked(int slotIndex) { + ST_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); + if (slotIndex == mCurrentTexture) { + mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; + } + 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; + ConsumerBase::freeBufferLocked(slotIndex); +} + +void GLConsumer::abandonLocked() { + ST_LOGV("abandonLocked"); + mCurrentTextureBuf.clear(); + ConsumerBase::abandonLocked(); +} + +void GLConsumer::setName(const String8& name) { + Mutex::Autolock _l(mMutex); + mName = name; + mBufferQueue->setConsumerName(name); +} + +status_t GLConsumer::setDefaultBufferFormat(uint32_t defaultFormat) { + Mutex::Autolock lock(mMutex); + return mBufferQueue->setDefaultBufferFormat(defaultFormat); +} + +status_t GLConsumer::setConsumerUsageBits(uint32_t usage) { + Mutex::Autolock lock(mMutex); + usage |= DEFAULT_USAGE_FLAGS; + return mBufferQueue->setConsumerUsageBits(usage); +} + +status_t GLConsumer::setTransformHint(uint32_t hint) { + Mutex::Autolock lock(mMutex); + return mBufferQueue->setTransformHint(hint); +} + +// Used for refactoring BufferQueue from GLConsumer +// Should not be in final interface once users of GLConsumer are clean up. +status_t GLConsumer::setSynchronousMode(bool enabled) { + Mutex::Autolock lock(mMutex); + return mBufferQueue->setSynchronousMode(enabled); +} + +void GLConsumer::dumpLocked(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); + result.append(buffer); + + ConsumerBase::dumpLocked(result, prefix, buffer, size); +} + +static void mtxMul(float out[16], const float a[16], const float b[16]) { + out[0] = a[0]*b[0] + a[4]*b[1] + a[8]*b[2] + a[12]*b[3]; + out[1] = a[1]*b[0] + a[5]*b[1] + a[9]*b[2] + a[13]*b[3]; + out[2] = a[2]*b[0] + a[6]*b[1] + a[10]*b[2] + a[14]*b[3]; + out[3] = a[3]*b[0] + a[7]*b[1] + a[11]*b[2] + a[15]*b[3]; + + out[4] = a[0]*b[4] + a[4]*b[5] + a[8]*b[6] + a[12]*b[7]; + out[5] = a[1]*b[4] + a[5]*b[5] + a[9]*b[6] + a[13]*b[7]; + out[6] = a[2]*b[4] + a[6]*b[5] + a[10]*b[6] + a[14]*b[7]; + out[7] = a[3]*b[4] + a[7]*b[5] + a[11]*b[6] + a[15]*b[7]; + + out[8] = a[0]*b[8] + a[4]*b[9] + a[8]*b[10] + a[12]*b[11]; + out[9] = a[1]*b[8] + a[5]*b[9] + a[9]*b[10] + a[13]*b[11]; + out[10] = a[2]*b[8] + a[6]*b[9] + a[10]*b[10] + a[14]*b[11]; + out[11] = a[3]*b[8] + a[7]*b[9] + a[11]*b[10] + a[15]*b[11]; + + out[12] = a[0]*b[12] + a[4]*b[13] + a[8]*b[14] + a[12]*b[15]; + out[13] = a[1]*b[12] + a[5]*b[13] + a[9]*b[14] + a[13]*b[15]; + out[14] = a[2]*b[12] + a[6]*b[13] + a[10]*b[14] + a[14]*b[15]; + out[15] = a[3]*b[12] + a[7]*b[13] + a[11]*b[14] + a[15]*b[15]; +} + +}; // namespace android diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp new file mode 100644 index 0000000000..923e394616 --- /dev/null +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2010 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. + */ + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +namespace android { +// ---------------------------------------------------------------------------- + +enum { + REQUEST_BUFFER = IBinder::FIRST_CALL_TRANSACTION, + SET_BUFFER_COUNT, + DEQUEUE_BUFFER, + QUEUE_BUFFER, + CANCEL_BUFFER, + QUERY, + SET_SYNCHRONOUS_MODE, + CONNECT, + DISCONNECT, +}; + + +class BpGraphicBufferProducer : public BpInterface +{ +public: + BpGraphicBufferProducer(const sp& impl) + : BpInterface(impl) + { + } + + virtual status_t requestBuffer(int bufferIdx, sp* buf) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeInt32(bufferIdx); + status_t result =remote()->transact(REQUEST_BUFFER, data, &reply); + if (result != NO_ERROR) { + return result; + } + bool nonNull = reply.readInt32(); + if (nonNull) { + *buf = new GraphicBuffer(); + reply.read(**buf); + } + result = reply.readInt32(); + return result; + } + + virtual status_t setBufferCount(int bufferCount) + { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeInt32(bufferCount); + status_t result =remote()->transact(SET_BUFFER_COUNT, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.readInt32(); + return result; + } + + virtual status_t dequeueBuffer(int *buf, sp& fence, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeInt32(w); + data.writeInt32(h); + data.writeInt32(format); + data.writeInt32(usage); + status_t result = remote()->transact(DEQUEUE_BUFFER, data, &reply); + if (result != NO_ERROR) { + return result; + } + *buf = reply.readInt32(); + fence.clear(); + bool hasFence = reply.readInt32(); + if (hasFence) { + fence = new Fence(); + reply.read(*fence.get()); + } + result = reply.readInt32(); + return result; + } + + virtual status_t queueBuffer(int buf, + const QueueBufferInput& input, QueueBufferOutput* output) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeInt32(buf); + data.write(input); + status_t result = remote()->transact(QUEUE_BUFFER, data, &reply); + if (result != NO_ERROR) { + return result; + } + memcpy(output, reply.readInplace(sizeof(*output)), sizeof(*output)); + result = reply.readInt32(); + return result; + } + + virtual void cancelBuffer(int buf, sp fence) { + Parcel data, reply; + bool hasFence = fence.get() && fence->isValid(); + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeInt32(buf); + data.writeInt32(hasFence); + if (hasFence) { + data.write(*fence.get()); + } + remote()->transact(CANCEL_BUFFER, data, &reply); + } + + virtual int query(int what, int* value) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeInt32(what); + status_t result = remote()->transact(QUERY, data, &reply); + if (result != NO_ERROR) { + return result; + } + value[0] = reply.readInt32(); + result = reply.readInt32(); + return result; + } + + virtual status_t setSynchronousMode(bool enabled) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeInt32(enabled); + status_t result = remote()->transact(SET_SYNCHRONOUS_MODE, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.readInt32(); + return result; + } + + virtual status_t connect(int api, QueueBufferOutput* output) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeInt32(api); + status_t result = remote()->transact(CONNECT, data, &reply); + if (result != NO_ERROR) { + return result; + } + memcpy(output, reply.readInplace(sizeof(*output)), sizeof(*output)); + result = reply.readInt32(); + return result; + } + + virtual status_t disconnect(int api) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeInt32(api); + status_t result =remote()->transact(DISCONNECT, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.readInt32(); + return result; + } +}; + +IMPLEMENT_META_INTERFACE(GraphicBufferProducer, "android.gui.SurfaceTexture"); + +// ---------------------------------------------------------------------- + +status_t BnGraphicBufferProducer::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case REQUEST_BUFFER: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + int bufferIdx = data.readInt32(); + sp buffer; + int result = requestBuffer(bufferIdx, &buffer); + reply->writeInt32(buffer != 0); + if (buffer != 0) { + reply->write(*buffer); + } + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_BUFFER_COUNT: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + int bufferCount = data.readInt32(); + int result = setBufferCount(bufferCount); + reply->writeInt32(result); + return NO_ERROR; + } break; + case DEQUEUE_BUFFER: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + uint32_t w = data.readInt32(); + uint32_t h = data.readInt32(); + uint32_t format = data.readInt32(); + uint32_t usage = data.readInt32(); + int buf; + sp fence; + int result = dequeueBuffer(&buf, fence, w, h, format, usage); + bool hasFence = fence.get() && fence->isValid(); + reply->writeInt32(buf); + reply->writeInt32(hasFence); + if (hasFence) { + reply->write(*fence.get()); + } + reply->writeInt32(result); + return NO_ERROR; + } break; + case QUEUE_BUFFER: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + int buf = data.readInt32(); + QueueBufferInput input(data); + QueueBufferOutput* const output = + reinterpret_cast( + reply->writeInplace(sizeof(QueueBufferOutput))); + status_t result = queueBuffer(buf, input, output); + reply->writeInt32(result); + return NO_ERROR; + } break; + case CANCEL_BUFFER: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + int buf = data.readInt32(); + sp fence; + bool hasFence = data.readInt32(); + if (hasFence) { + fence = new Fence(); + data.read(*fence.get()); + } + cancelBuffer(buf, fence); + return NO_ERROR; + } break; + case QUERY: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + int value; + int what = data.readInt32(); + int res = query(what, &value); + reply->writeInt32(value); + reply->writeInt32(res); + return NO_ERROR; + } break; + case SET_SYNCHRONOUS_MODE: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + bool enabled = data.readInt32(); + status_t res = setSynchronousMode(enabled); + reply->writeInt32(res); + return NO_ERROR; + } break; + case CONNECT: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + int api = data.readInt32(); + QueueBufferOutput* const output = + reinterpret_cast( + reply->writeInplace(sizeof(QueueBufferOutput))); + status_t res = connect(api, output); + reply->writeInt32(res); + return NO_ERROR; + } break; + case DISCONNECT: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + int api = data.readInt32(); + status_t res = disconnect(api); + reply->writeInt32(res); + return NO_ERROR; + } break; + } + return BBinder::onTransact(code, data, reply, flags); +} + +// ---------------------------------------------------------------------------- + +static bool isValid(const sp& fence) { + return fence.get() && fence->isValid(); +} + +IGraphicBufferProducer::QueueBufferInput::QueueBufferInput(const Parcel& parcel) { + parcel.read(*this); +} + +size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const +{ + return sizeof(timestamp) + + sizeof(crop) + + sizeof(scalingMode) + + sizeof(transform) + + sizeof(bool) + + (isValid(fence) ? fence->getFlattenedSize() : 0); +} + +size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const +{ + return isValid(fence) ? fence->getFdCount() : 0; +} + +status_t IGraphicBufferProducer::QueueBufferInput::flatten(void* buffer, size_t size, + int fds[], size_t count) const +{ + status_t err = NO_ERROR; + bool haveFence = isValid(fence); + char* p = (char*)buffer; + memcpy(p, ×tamp, sizeof(timestamp)); p += sizeof(timestamp); + memcpy(p, &crop, sizeof(crop)); p += sizeof(crop); + memcpy(p, &scalingMode, sizeof(scalingMode)); p += sizeof(scalingMode); + memcpy(p, &transform, sizeof(transform)); p += sizeof(transform); + memcpy(p, &haveFence, sizeof(haveFence)); p += sizeof(haveFence); + if (haveFence) { + err = fence->flatten(p, size - (p - (char*)buffer), fds, count); + } + return err; +} + +status_t IGraphicBufferProducer::QueueBufferInput::unflatten(void const* buffer, + size_t size, int fds[], size_t count) +{ + status_t err = NO_ERROR; + bool haveFence; + const char* p = (const char*)buffer; + memcpy(×tamp, p, sizeof(timestamp)); p += sizeof(timestamp); + memcpy(&crop, p, sizeof(crop)); p += sizeof(crop); + memcpy(&scalingMode, p, sizeof(scalingMode)); p += sizeof(scalingMode); + memcpy(&transform, p, sizeof(transform)); p += sizeof(transform); + memcpy(&haveFence, p, sizeof(haveFence)); p += sizeof(haveFence); + if (haveFence) { + fence = new Fence(); + err = fence->unflatten(p, size - (p - (const char*)buffer), fds, count); + } + return err; +} + +}; // namespace android diff --git a/libs/gui/ISurface.cpp b/libs/gui/ISurface.cpp index c2ea183077..8c25f4513d 100644 --- a/libs/gui/ISurface.cpp +++ b/libs/gui/ISurface.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include namespace android { @@ -37,11 +37,11 @@ public: { } - virtual sp getSurfaceTexture() const { + virtual sp getSurfaceTexture() const { Parcel data, reply; data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); remote()->transact(GET_SURFACE_TEXTURE, data, &reply); - return interface_cast(reply.readStrongBinder()); + return interface_cast(reply.readStrongBinder()); } }; diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 85a94882ee..72b627746f 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include @@ -124,7 +124,7 @@ public: } virtual bool authenticateSurfaceTexture( - const sp& surfaceTexture) const + const sp& bufferProducer) const { Parcel data, reply; int err = NO_ERROR; @@ -135,7 +135,7 @@ public: "interface descriptor: %s (%d)", strerror(-err), -err); return false; } - err = data.writeStrongBinder(surfaceTexture->asBinder()); + err = data.writeStrongBinder(bufferProducer->asBinder()); if (err != NO_ERROR) { ALOGE("ISurfaceComposer::authenticateSurfaceTexture: error writing " "strong binder to parcel: %s (%d)", strerror(-err), -err); @@ -288,9 +288,9 @@ status_t BnSurfaceComposer::onTransact( } break; case AUTHENTICATE_SURFACE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp surfaceTexture = - interface_cast(data.readStrongBinder()); - int32_t result = authenticateSurfaceTexture(surfaceTexture) ? 1 : 0; + sp bufferProducer = + interface_cast(data.readStrongBinder()); + int32_t result = authenticateSurfaceTexture(bufferProducer) ? 1 : 0; reply->writeInt32(result); } break; case CREATE_DISPLAY_EVENT_CONNECTION: { diff --git a/libs/gui/ISurfaceTexture.cpp b/libs/gui/ISurfaceTexture.cpp deleted file mode 100644 index a0b1e7446c..0000000000 --- a/libs/gui/ISurfaceTexture.cpp +++ /dev/null @@ -1,350 +0,0 @@ -/* - * Copyright (C) 2010 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. - */ - -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include - -namespace android { -// ---------------------------------------------------------------------------- - -enum { - REQUEST_BUFFER = IBinder::FIRST_CALL_TRANSACTION, - SET_BUFFER_COUNT, - DEQUEUE_BUFFER, - QUEUE_BUFFER, - CANCEL_BUFFER, - QUERY, - SET_SYNCHRONOUS_MODE, - CONNECT, - DISCONNECT, -}; - - -class BpSurfaceTexture : public BpInterface -{ -public: - BpSurfaceTexture(const sp& impl) - : BpInterface(impl) - { - } - - virtual status_t requestBuffer(int bufferIdx, sp* buf) { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); - data.writeInt32(bufferIdx); - status_t result =remote()->transact(REQUEST_BUFFER, data, &reply); - if (result != NO_ERROR) { - return result; - } - bool nonNull = reply.readInt32(); - if (nonNull) { - *buf = new GraphicBuffer(); - reply.read(**buf); - } - result = reply.readInt32(); - return result; - } - - virtual status_t setBufferCount(int bufferCount) - { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); - data.writeInt32(bufferCount); - status_t result =remote()->transact(SET_BUFFER_COUNT, data, &reply); - if (result != NO_ERROR) { - return result; - } - result = reply.readInt32(); - return result; - } - - virtual status_t dequeueBuffer(int *buf, sp& fence, - uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); - data.writeInt32(w); - data.writeInt32(h); - data.writeInt32(format); - data.writeInt32(usage); - status_t result = remote()->transact(DEQUEUE_BUFFER, data, &reply); - if (result != NO_ERROR) { - return result; - } - *buf = reply.readInt32(); - fence.clear(); - bool hasFence = reply.readInt32(); - if (hasFence) { - fence = new Fence(); - reply.read(*fence.get()); - } - result = reply.readInt32(); - return result; - } - - virtual status_t queueBuffer(int buf, - const QueueBufferInput& input, QueueBufferOutput* output) { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); - data.writeInt32(buf); - data.write(input); - status_t result = remote()->transact(QUEUE_BUFFER, data, &reply); - if (result != NO_ERROR) { - return result; - } - memcpy(output, reply.readInplace(sizeof(*output)), sizeof(*output)); - result = reply.readInt32(); - return result; - } - - virtual void cancelBuffer(int buf, sp fence) { - Parcel data, reply; - bool hasFence = fence.get() && fence->isValid(); - data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); - data.writeInt32(buf); - data.writeInt32(hasFence); - if (hasFence) { - data.write(*fence.get()); - } - remote()->transact(CANCEL_BUFFER, data, &reply); - } - - virtual int query(int what, int* value) { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); - data.writeInt32(what); - status_t result = remote()->transact(QUERY, data, &reply); - if (result != NO_ERROR) { - return result; - } - value[0] = reply.readInt32(); - result = reply.readInt32(); - return result; - } - - virtual status_t setSynchronousMode(bool enabled) { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); - data.writeInt32(enabled); - status_t result = remote()->transact(SET_SYNCHRONOUS_MODE, data, &reply); - if (result != NO_ERROR) { - return result; - } - result = reply.readInt32(); - return result; - } - - virtual status_t connect(int api, QueueBufferOutput* output) { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); - data.writeInt32(api); - status_t result = remote()->transact(CONNECT, data, &reply); - if (result != NO_ERROR) { - return result; - } - memcpy(output, reply.readInplace(sizeof(*output)), sizeof(*output)); - result = reply.readInt32(); - return result; - } - - virtual status_t disconnect(int api) { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); - data.writeInt32(api); - status_t result =remote()->transact(DISCONNECT, data, &reply); - if (result != NO_ERROR) { - return result; - } - result = reply.readInt32(); - return result; - } -}; - -IMPLEMENT_META_INTERFACE(SurfaceTexture, "android.gui.SurfaceTexture"); - -// ---------------------------------------------------------------------- - -status_t BnSurfaceTexture::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - switch(code) { - case REQUEST_BUFFER: { - CHECK_INTERFACE(ISurfaceTexture, data, reply); - int bufferIdx = data.readInt32(); - sp buffer; - int result = requestBuffer(bufferIdx, &buffer); - reply->writeInt32(buffer != 0); - if (buffer != 0) { - reply->write(*buffer); - } - reply->writeInt32(result); - return NO_ERROR; - } break; - case SET_BUFFER_COUNT: { - CHECK_INTERFACE(ISurfaceTexture, data, reply); - int bufferCount = data.readInt32(); - int result = setBufferCount(bufferCount); - reply->writeInt32(result); - return NO_ERROR; - } break; - case DEQUEUE_BUFFER: { - CHECK_INTERFACE(ISurfaceTexture, data, reply); - uint32_t w = data.readInt32(); - uint32_t h = data.readInt32(); - uint32_t format = data.readInt32(); - uint32_t usage = data.readInt32(); - int buf; - sp fence; - int result = dequeueBuffer(&buf, fence, w, h, format, usage); - bool hasFence = fence.get() && fence->isValid(); - reply->writeInt32(buf); - reply->writeInt32(hasFence); - if (hasFence) { - reply->write(*fence.get()); - } - reply->writeInt32(result); - return NO_ERROR; - } break; - case QUEUE_BUFFER: { - CHECK_INTERFACE(ISurfaceTexture, data, reply); - int buf = data.readInt32(); - QueueBufferInput input(data); - QueueBufferOutput* const output = - reinterpret_cast( - reply->writeInplace(sizeof(QueueBufferOutput))); - status_t result = queueBuffer(buf, input, output); - reply->writeInt32(result); - return NO_ERROR; - } break; - case CANCEL_BUFFER: { - CHECK_INTERFACE(ISurfaceTexture, data, reply); - int buf = data.readInt32(); - sp fence; - bool hasFence = data.readInt32(); - if (hasFence) { - fence = new Fence(); - data.read(*fence.get()); - } - cancelBuffer(buf, fence); - return NO_ERROR; - } break; - case QUERY: { - CHECK_INTERFACE(ISurfaceTexture, data, reply); - int value; - int what = data.readInt32(); - int res = query(what, &value); - reply->writeInt32(value); - reply->writeInt32(res); - return NO_ERROR; - } break; - case SET_SYNCHRONOUS_MODE: { - CHECK_INTERFACE(ISurfaceTexture, data, reply); - bool enabled = data.readInt32(); - status_t res = setSynchronousMode(enabled); - reply->writeInt32(res); - return NO_ERROR; - } break; - case CONNECT: { - CHECK_INTERFACE(ISurfaceTexture, data, reply); - int api = data.readInt32(); - QueueBufferOutput* const output = - reinterpret_cast( - reply->writeInplace(sizeof(QueueBufferOutput))); - status_t res = connect(api, output); - reply->writeInt32(res); - return NO_ERROR; - } break; - case DISCONNECT: { - CHECK_INTERFACE(ISurfaceTexture, data, reply); - int api = data.readInt32(); - status_t res = disconnect(api); - reply->writeInt32(res); - return NO_ERROR; - } break; - } - return BBinder::onTransact(code, data, reply, flags); -} - -// ---------------------------------------------------------------------------- - -static bool isValid(const sp& fence) { - return fence.get() && fence->isValid(); -} - -ISurfaceTexture::QueueBufferInput::QueueBufferInput(const Parcel& parcel) { - parcel.read(*this); -} - -size_t ISurfaceTexture::QueueBufferInput::getFlattenedSize() const -{ - return sizeof(timestamp) - + sizeof(crop) - + sizeof(scalingMode) - + sizeof(transform) - + sizeof(bool) - + (isValid(fence) ? fence->getFlattenedSize() : 0); -} - -size_t ISurfaceTexture::QueueBufferInput::getFdCount() const -{ - return isValid(fence) ? fence->getFdCount() : 0; -} - -status_t ISurfaceTexture::QueueBufferInput::flatten(void* buffer, size_t size, - int fds[], size_t count) const -{ - status_t err = NO_ERROR; - bool haveFence = isValid(fence); - char* p = (char*)buffer; - memcpy(p, ×tamp, sizeof(timestamp)); p += sizeof(timestamp); - memcpy(p, &crop, sizeof(crop)); p += sizeof(crop); - memcpy(p, &scalingMode, sizeof(scalingMode)); p += sizeof(scalingMode); - memcpy(p, &transform, sizeof(transform)); p += sizeof(transform); - memcpy(p, &haveFence, sizeof(haveFence)); p += sizeof(haveFence); - if (haveFence) { - err = fence->flatten(p, size - (p - (char*)buffer), fds, count); - } - return err; -} - -status_t ISurfaceTexture::QueueBufferInput::unflatten(void const* buffer, - size_t size, int fds[], size_t count) -{ - status_t err = NO_ERROR; - bool haveFence; - const char* p = (const char*)buffer; - memcpy(×tamp, p, sizeof(timestamp)); p += sizeof(timestamp); - memcpy(&crop, p, sizeof(crop)); p += sizeof(crop); - memcpy(&scalingMode, p, sizeof(scalingMode)); p += sizeof(scalingMode); - memcpy(&transform, p, sizeof(transform)); p += sizeof(transform); - memcpy(&haveFence, p, sizeof(haveFence)); p += sizeof(haveFence); - if (haveFence) { - fence = new Fence(); - err = fence->unflatten(p, size - (p - (const char*)buffer), fds, count); - } - return err; -} - -}; // namespace android diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index e2604f8e20..5e5edcd2db 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include namespace android { @@ -74,7 +74,7 @@ status_t DisplayState::write(Parcel& output) const { status_t DisplayState::read(const Parcel& input) { token = input.readStrongBinder(); - surface = interface_cast(input.readStrongBinder()); + surface = interface_cast(input.readStrongBinder()); what = input.readInt32(); layerStack = input.readInt32(); orientation = input.readInt32(); diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 174506107d..51d37b3cf7 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -179,7 +179,7 @@ status_t SurfaceControl::writeSurfaceToParcel( identity = control->mIdentity; } parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL); - parcel->writeStrongBinder(NULL); // NULL ISurfaceTexture in this case. + parcel->writeStrongBinder(NULL); // NULL IGraphicBufferProducer in this case. parcel->writeInt32(identity); return NO_ERROR; } @@ -205,7 +205,7 @@ Surface::Surface(const sp& surface) mSurface(surface->mSurface), mIdentity(surface->mIdentity) { - sp st; + sp st; if (mSurface != NULL) { st = mSurface->getSurfaceTexture(); } @@ -217,9 +217,9 @@ Surface::Surface(const Parcel& parcel, const sp& ref) { mSurface = interface_cast(ref); sp st_binder(parcel.readStrongBinder()); - sp st; + sp st; if (st_binder != NULL) { - st = interface_cast(st_binder); + st = interface_cast(st_binder); } else if (mSurface != NULL) { st = mSurface->getSurfaceTexture(); } @@ -228,7 +228,7 @@ Surface::Surface(const Parcel& parcel, const sp& ref) init(st); } -Surface::Surface(const sp& st) +Surface::Surface(const sp& st) : SurfaceTextureClient(), mSurface(NULL), mIdentity(0) @@ -240,7 +240,7 @@ status_t Surface::writeToParcel( const sp& surface, Parcel* parcel) { sp sur; - sp st; + sp st; uint32_t identity = 0; if (Surface::isValid(surface)) { sur = surface->mSurface; @@ -249,8 +249,8 @@ status_t Surface::writeToParcel( } else if (surface != 0 && (surface->mSurface != NULL || surface->getISurfaceTexture() != NULL)) { - ALOGE("Parceling invalid surface with non-NULL ISurface/ISurfaceTexture as NULL: " - "mSurface = %p, surfaceTexture = %p, mIdentity = %d, ", + ALOGE("Parceling invalid surface with non-NULL ISurface/IGraphicBufferProducer " + "as NULL: mSurface = %p, bufferProducer = %p, mIdentity = %d, ", surface->mSurface.get(), surface->getISurfaceTexture().get(), surface->mIdentity); } @@ -275,7 +275,7 @@ sp Surface::readFromParcel(const Parcel& data) { } else { // The Surface was found in the cache, but we still should clear any // remaining data from the parcel. - data.readStrongBinder(); // ISurfaceTexture + data.readStrongBinder(); // IGraphicBufferProducer data.readInt32(); // identity } if (surface->mSurface == NULL && surface->getISurfaceTexture() == NULL) { @@ -296,12 +296,12 @@ void Surface::cleanCachedSurfacesLocked() { } } -void Surface::init(const sp& surfaceTexture) +void Surface::init(const sp& bufferProducer) { - if (mSurface != NULL || surfaceTexture != NULL) { - ALOGE_IF(surfaceTexture==0, "got a NULL ISurfaceTexture from ISurface"); - if (surfaceTexture != NULL) { - setISurfaceTexture(surfaceTexture); + if (mSurface != NULL || bufferProducer != NULL) { + ALOGE_IF(bufferProducer==0, "got a NULL IGraphicBufferProducer from ISurface"); + if (bufferProducer != NULL) { + setISurfaceTexture(bufferProducer); setUsage(GraphicBuffer::USAGE_HW_RENDER); } @@ -328,7 +328,7 @@ bool Surface::isValid() { return getISurfaceTexture() != NULL; } -sp Surface::getSurfaceTexture() { +sp Surface::getSurfaceTexture() { return getISurfaceTexture(); } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 746057b429..0c6881a69d 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -157,7 +157,8 @@ public: status_t setLayerStack(const sp& client, SurfaceID id, uint32_t layerStack); - void setDisplaySurface(const sp& token, const sp& surface); + void setDisplaySurface(const sp& token, + const sp& bufferProducer); void setDisplayLayerStack(const sp& token, uint32_t layerStack); void setDisplayProjection(const sp& token, uint32_t orientation, @@ -386,10 +387,10 @@ DisplayState& Composer::getDisplayStateLocked(const sp& token) { } void Composer::setDisplaySurface(const sp& token, - const sp& surface) { + const sp& bufferProducer) { Mutex::Autolock _l(mLock); DisplayState& s(getDisplayStateLocked(token)); - s.surface = surface; + s.surface = bufferProducer; s.what |= DisplayState::eSurfaceChanged; } @@ -571,8 +572,8 @@ status_t SurfaceComposerClient::setMatrix(SurfaceID id, float dsdx, float dtdx, // ---------------------------------------------------------------------------- void SurfaceComposerClient::setDisplaySurface(const sp& token, - const sp& surface) { - Composer::getInstance().setDisplaySurface(token, surface); + const sp& bufferProducer) { + Composer::getInstance().setDisplaySurface(token, bufferProducer); } void SurfaceComposerClient::setDisplayLayerStack(const sp& token, diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp deleted file mode 100644 index ee3079e564..0000000000 --- a/libs/gui/SurfaceTexture.cpp +++ /dev/null @@ -1,961 +0,0 @@ -/* - * Copyright (C) 2010 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 LOG_TAG "SurfaceTexture" -#define ATRACE_TAG ATRACE_TAG_GRAPHICS -//#define LOG_NDEBUG 0 - -#define GL_GLEXT_PROTOTYPES -#define EGL_EGLEXT_PROTOTYPES - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include - -namespace android { - -// This compile option makes SurfaceTexture use the -// EGL_ANDROID_native_fence_sync extension to create Android native fences to -// signal when all GLES reads for a given buffer have completed. It is not -// compatible with using the EGL_KHR_fence_sync extension for the same -// purpose. -#ifdef USE_NATIVE_FENCE_SYNC -#ifdef USE_FENCE_SYNC -#error "USE_NATIVE_FENCE_SYNC and USE_FENCE_SYNC are incompatible" -#endif -const bool SurfaceTexture::sUseNativeFenceSync = true; -#else -const bool SurfaceTexture::sUseNativeFenceSync = false; -#endif - -// This compile option makes SurfaceTexture use the EGL_ANDROID_sync_wait -// extension to insert server-side waits into the GLES command stream. This -// feature requires the EGL_ANDROID_native_fence_sync and -// EGL_ANDROID_wait_sync extensions. -#ifdef USE_WAIT_SYNC -static const bool useWaitSync = true; -#else -static const bool useWaitSync = false; -#endif - -// Macros for including the SurfaceTexture name in log messages -#define ST_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__) -#define ST_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__) -#define ST_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__) -#define ST_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__) -#define ST_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__) - -// Transform matrices -static float mtxIdentity[16] = { - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1, -}; -static float mtxFlipH[16] = { - -1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 1, 0, 0, 1, -}; -static float mtxFlipV[16] = { - 1, 0, 0, 0, - 0, -1, 0, 0, - 0, 0, 1, 0, - 0, 1, 0, 1, -}; -static float mtxRot90[16] = { - 0, 1, 0, 0, - -1, 0, 0, 0, - 0, 0, 1, 0, - 1, 0, 0, 1, -}; -static float mtxRot180[16] = { - -1, 0, 0, 0, - 0, -1, 0, 0, - 0, 0, 1, 0, - 1, 1, 0, 1, -}; -static float mtxRot270[16] = { - 0, -1, 0, 0, - 1, 0, 0, 0, - 0, 0, 1, 0, - 0, 1, 0, 1, -}; - -static void mtxMul(float out[16], const float a[16], const float b[16]); - - -SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode, - GLenum texTarget, bool useFenceSync, const sp &bufferQueue) : - ConsumerBase(bufferQueue == 0 ? new BufferQueue(allowSynchronousMode) : bufferQueue), - mCurrentTransform(0), - mCurrentTimestamp(0), - mFilteringEnabled(true), - mTexName(tex), -#ifdef USE_FENCE_SYNC - mUseFenceSync(useFenceSync), -#else - mUseFenceSync(false), -#endif - mTexTarget(texTarget), - mEglDisplay(EGL_NO_DISPLAY), - mEglContext(EGL_NO_CONTEXT), - mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), - mAttached(true) -{ - ST_LOGV("SurfaceTexture"); - - memcpy(mCurrentTransformMatrix, mtxIdentity, - sizeof(mCurrentTransformMatrix)); - - mBufferQueue->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); -} - -status_t SurfaceTexture::setDefaultMaxBufferCount(int bufferCount) { - Mutex::Autolock lock(mMutex); - return mBufferQueue->setDefaultMaxBufferCount(bufferCount); -} - - -status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) -{ - Mutex::Autolock lock(mMutex); - mDefaultWidth = w; - mDefaultHeight = h; - return mBufferQueue->setDefaultBufferSize(w, h); -} - -status_t SurfaceTexture::updateTexImage() { - 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, and wait until it's ready. - return bindTextureImageLocked(); -} - -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) { - // 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) { - if (!eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage)) { - ST_LOGW("acquireBufferLocked: eglDestroyImageKHR failed for slot=%d", - slot); - // keep going - } - mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR; - } - } - - return NO_ERROR; -} - -status_t SurfaceTexture::releaseBufferLocked(int buf, EGLDisplay display, - EGLSyncKHR eglFence) { - status_t err = ConsumerBase::releaseBufferLocked(buf, display, eglFence); - - mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR; - - return err; -} - -status_t SurfaceTexture::releaseAndUpdateLocked(const BufferQueue::BufferItem& item) -{ - status_t err = NO_ERROR; - - if (!mAttached) { - 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::bindTextureImageLocked() { - 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; - } - status_t err = bindUnslottedBufferLocked(mEglDisplay); - if (err != NO_ERROR) { - return err; - } - } 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; - } - } - - // Wait for the new buffer to be ready. - return doGLFenceWaitLocked(); - -} - -status_t SurfaceTexture::checkAndUpdateEglStateLocked() { - EGLDisplay dpy = eglGetCurrentDisplay(); - EGLContext ctx = eglGetCurrentContext(); - - if ((mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) || - dpy == EGL_NO_DISPLAY) { - ST_LOGE("checkAndUpdateEglState: invalid current EGLDisplay"); - return INVALID_OPERATION; - } - - if ((mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) || - ctx == EGL_NO_CONTEXT) { - ST_LOGE("checkAndUpdateEglState: invalid current EGLContext"); - return INVALID_OPERATION; - } - - mEglDisplay = dpy; - mEglContext = ctx; - return NO_ERROR; -} - -void SurfaceTexture::setReleaseFence(int fenceFd) { - sp fence(new Fence(fenceFd)); - if (fenceFd == -1 || mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) - return; - status_t err = addReleaseFence(mCurrentTexture, fence); - if (err != OK) { - ST_LOGE("setReleaseFence: failed to add the fence: %s (%d)", - strerror(-err), err); - } -} - -status_t SurfaceTexture::detachFromContext() { - ATRACE_CALL(); - ST_LOGV("detachFromContext"); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - ST_LOGE("detachFromContext: abandoned SurfaceTexture"); - return NO_INIT; - } - - if (!mAttached) { - ST_LOGE("detachFromContext: SurfaceTexture is not attached to a " - "context"); - return INVALID_OPERATION; - } - - EGLDisplay dpy = eglGetCurrentDisplay(); - EGLContext ctx = eglGetCurrentContext(); - - if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) { - ST_LOGE("detachFromContext: invalid current EGLDisplay"); - return INVALID_OPERATION; - } - - if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) { - ST_LOGE("detachFromContext: invalid current EGLContext"); - return INVALID_OPERATION; - } - - if (dpy != EGL_NO_DISPLAY && ctx != EGL_NO_CONTEXT) { - status_t err = syncForReleaseLocked(dpy); - if (err != OK) { - return err; - } - - glDeleteTextures(1, &mTexName); - } - - // Because we're giving up the EGLDisplay we need to free all the EGLImages - // that are associated with it. They'll be recreated when the - // 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; - if (img != EGL_NO_IMAGE_KHR) { - eglDestroyImageKHR(mEglDisplay, img); - mEglSlots[i].mEglImage = EGL_NO_IMAGE_KHR; - } - } - - mEglDisplay = EGL_NO_DISPLAY; - mEglContext = EGL_NO_CONTEXT; - mAttached = false; - - return OK; -} - -status_t SurfaceTexture::attachToContext(GLuint tex) { - ATRACE_CALL(); - ST_LOGV("attachToContext"); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - ST_LOGE("attachToContext: abandoned SurfaceTexture"); - return NO_INIT; - } - - if (mAttached) { - ST_LOGE("attachToContext: SurfaceTexture is already attached to a " - "context"); - return INVALID_OPERATION; - } - - EGLDisplay dpy = eglGetCurrentDisplay(); - EGLContext ctx = eglGetCurrentContext(); - - if (dpy == EGL_NO_DISPLAY) { - ST_LOGE("attachToContext: invalid current EGLDisplay"); - return INVALID_OPERATION; - } - - if (ctx == EGL_NO_CONTEXT) { - ST_LOGE("attachToContext: invalid current EGLContext"); - return INVALID_OPERATION; - } - - // We need to bind the texture regardless of whether there's a current - // buffer. - glBindTexture(mTexTarget, tex); - - if (mCurrentTextureBuf != NULL) { - // 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. - status_t err = bindUnslottedBufferLocked(dpy); - if (err != NO_ERROR) { - return err; - } - } - - mEglDisplay = dpy; - mEglContext = ctx; - mTexName = tex; - mAttached = true; - - 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"); - - if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - if (sUseNativeFenceSync) { - EGLSyncKHR sync = eglCreateSyncKHR(dpy, - EGL_SYNC_NATIVE_FENCE_ANDROID, NULL); - if (sync == EGL_NO_SYNC_KHR) { - ST_LOGE("syncForReleaseLocked: error creating EGL fence: %#x", - eglGetError()); - return UNKNOWN_ERROR; - } - glFlush(); - int fenceFd = eglDupNativeFenceFDANDROID(dpy, sync); - eglDestroySyncKHR(dpy, sync); - if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { - ST_LOGE("syncForReleaseLocked: error dup'ing native fence " - "fd: %#x", eglGetError()); - return UNKNOWN_ERROR; - } - sp fence(new Fence(fenceFd)); - status_t err = addReleaseFenceLocked(mCurrentTexture, fence); - if (err != OK) { - ST_LOGE("syncForReleaseLocked: error adding release fence: " - "%s (%d)", strerror(-err), err); - return err; - } - } else if (mUseFenceSync) { - EGLSyncKHR fence = mEglSlots[mCurrentTexture].mEglFence; - 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 outstanding buffer accesses have completed - // before the producer accesses it. - EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); - if (result == EGL_FALSE) { - ST_LOGE("syncForReleaseLocked: error waiting for previous " - "fence: %#x", eglGetError()); - return UNKNOWN_ERROR; - } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { - ST_LOGE("syncForReleaseLocked: timeout waiting for previous " - "fence"); - return TIMED_OUT; - } - eglDestroySyncKHR(dpy, fence); - } - - // Create a fence for the outstanding accesses in the current - // OpenGL ES context. - fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL); - if (fence == EGL_NO_SYNC_KHR) { - ST_LOGE("syncForReleaseLocked: error creating fence: %#x", - eglGetError()); - return UNKNOWN_ERROR; - } - glFlush(); - mEglSlots[mCurrentTexture].mEglFence = fence; - } - } - - return OK; -} - -bool SurfaceTexture::isExternalFormat(uint32_t format) -{ - switch (format) { - // supported YUV formats - case HAL_PIXEL_FORMAT_YV12: - // Legacy/deprecated YUV formats - case HAL_PIXEL_FORMAT_YCbCr_422_SP: - case HAL_PIXEL_FORMAT_YCrCb_420_SP: - case HAL_PIXEL_FORMAT_YCbCr_422_I: - return true; - } - - // Any OEM format needs to be considered - if (format>=0x100 && format<=0x1FF) - return true; - - return false; -} - -GLenum SurfaceTexture::getCurrentTextureTarget() const { - return mTexTarget; -} - -void SurfaceTexture::getTransformMatrix(float mtx[16]) { - Mutex::Autolock lock(mMutex); - memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix)); -} - -void SurfaceTexture::setFilteringEnabled(bool enabled) { - Mutex::Autolock lock(mMutex); - if (mAbandoned) { - ST_LOGE("setFilteringEnabled: SurfaceTexture is abandoned!"); - return; - } - bool needsRecompute = mFilteringEnabled != enabled; - mFilteringEnabled = enabled; - - if (needsRecompute && mCurrentTextureBuf==NULL) { - ST_LOGD("setFilteringEnabled called with mCurrentTextureBuf == NULL"); - } - - if (needsRecompute && mCurrentTextureBuf != NULL) { - computeCurrentTransformMatrixLocked(); - } -} - -void SurfaceTexture::computeCurrentTransformMatrixLocked() { - ST_LOGV("computeCurrentTransformMatrixLocked"); - - float xform[16]; - for (int i = 0; i < 16; i++) { - xform[i] = mtxIdentity[i]; - } - if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) { - float result[16]; - mtxMul(result, xform, mtxFlipH); - for (int i = 0; i < 16; i++) { - xform[i] = result[i]; - } - } - if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) { - float result[16]; - mtxMul(result, xform, mtxFlipV); - for (int i = 0; i < 16; i++) { - xform[i] = result[i]; - } - } - if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) { - float result[16]; - mtxMul(result, xform, mtxRot90); - for (int i = 0; i < 16; i++) { - xform[i] = result[i]; - } - } - - sp& buf(mCurrentTextureBuf); - - if (buf == NULL) { - ST_LOGD("computeCurrentTransformMatrixLocked: mCurrentTextureBuf is NULL"); - } - - Rect cropRect = mCurrentCrop; - float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f; - float bufferWidth = buf->getWidth(); - float bufferHeight = buf->getHeight(); - if (!cropRect.isEmpty()) { - float shrinkAmount = 0.0f; - if (mFilteringEnabled) { - // In order to prevent bilinear sampling beyond the edge of the - // crop rectangle we may need to shrink it by 2 texels in each - // dimension. Normally this would just need to take 1/2 a texel - // off each end, but because the chroma channels of YUV420 images - // are subsampled we may need to shrink the crop region by a whole - // texel on each side. - switch (buf->getPixelFormat()) { - case PIXEL_FORMAT_RGBA_8888: - case PIXEL_FORMAT_RGBX_8888: - case PIXEL_FORMAT_RGB_888: - case PIXEL_FORMAT_RGB_565: - case PIXEL_FORMAT_BGRA_8888: - case PIXEL_FORMAT_RGBA_5551: - case PIXEL_FORMAT_RGBA_4444: - // We know there's no subsampling of any channels, so we - // 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; - } - } - - // Only shrink the dimensions that are not the size of the buffer. - if (cropRect.width() < bufferWidth) { - tx = (float(cropRect.left) + shrinkAmount) / bufferWidth; - sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / - bufferWidth; - } - if (cropRect.height() < bufferHeight) { - ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / - bufferHeight; - sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / - bufferHeight; - } - } - float crop[16] = { - sx, 0, 0, 0, - 0, sy, 0, 0, - 0, 0, 1, 0, - tx, ty, 0, 1, - }; - - float mtxBeforeFlipV[16]; - mtxMul(mtxBeforeFlipV, crop, xform); - - // SurfaceFlinger expects the top of its window textures to be at a Y - // coordinate of 0, so SurfaceTexture must behave the same way. We don't - // want to expose this to applications, however, so we must add an - // additional vertical flip to the transform after all the other transforms. - mtxMul(mCurrentTransformMatrix, mtxFlipV, mtxBeforeFlipV); -} - -nsecs_t SurfaceTexture::getTimestamp() { - ST_LOGV("getTimestamp"); - Mutex::Autolock lock(mMutex); - return mCurrentTimestamp; -} - -EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy, - const sp& graphicBuffer) { - EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer(); - EGLint attrs[] = { - EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, - EGL_NONE, - }; - EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, - EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs); - if (image == EGL_NO_IMAGE_KHR) { - EGLint error = eglGetError(); - ST_LOGE("error creating EGLImage: %#x", error); - } - return image; -} - -sp SurfaceTexture::getCurrentBuffer() const { - Mutex::Autolock lock(mMutex); - return mCurrentTextureBuf; -} - -Rect SurfaceTexture::getCurrentCrop() const { - Mutex::Autolock lock(mMutex); - - Rect outCrop = mCurrentCrop; - if (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) { - int32_t newWidth = mCurrentCrop.width(); - int32_t newHeight = mCurrentCrop.height(); - - if (newWidth * mDefaultHeight > newHeight * mDefaultWidth) { - newWidth = newHeight * mDefaultWidth / mDefaultHeight; - ST_LOGV("too wide: newWidth = %d", newWidth); - } else if (newWidth * mDefaultHeight < newHeight * mDefaultWidth) { - newHeight = newWidth * mDefaultHeight / mDefaultWidth; - ST_LOGV("too tall: newHeight = %d", newHeight); - } - - // The crop is too wide - if (newWidth < mCurrentCrop.width()) { - int32_t dw = (newWidth - mCurrentCrop.width())/2; - outCrop.left -=dw; - outCrop.right += dw; - // The crop is too tall - } else if (newHeight < mCurrentCrop.height()) { - int32_t dh = (newHeight - mCurrentCrop.height())/2; - outCrop.top -= dh; - outCrop.bottom += dh; - } - - ST_LOGV("getCurrentCrop final crop [%d,%d,%d,%d]", - outCrop.left, outCrop.top, - outCrop.right,outCrop.bottom); - } - - return outCrop; -} - -uint32_t SurfaceTexture::getCurrentTransform() const { - Mutex::Autolock lock(mMutex); - return mCurrentTransform; -} - -uint32_t SurfaceTexture::getCurrentScalingMode() const { - Mutex::Autolock lock(mMutex); - return mCurrentScalingMode; -} - -sp SurfaceTexture::getCurrentFence() const { - Mutex::Autolock lock(mMutex); - return mCurrentFence; -} - -status_t SurfaceTexture::doGLFenceWait() const { - Mutex::Autolock lock(mMutex); - return doGLFenceWaitLocked(); -} - -status_t SurfaceTexture::doGLFenceWaitLocked() const { - - EGLDisplay dpy = eglGetCurrentDisplay(); - EGLContext ctx = eglGetCurrentContext(); - - if (mEglDisplay != dpy || mEglDisplay == EGL_NO_DISPLAY) { - ST_LOGE("doGLFenceWait: invalid current EGLDisplay"); - return INVALID_OPERATION; - } - - if (mEglContext != ctx || mEglContext == EGL_NO_CONTEXT) { - ST_LOGE("doGLFenceWait: invalid current EGLContext"); - return INVALID_OPERATION; - } - - if (mCurrentFence != NULL) { - if (useWaitSync) { - // Create an EGLSyncKHR from the current fence. - int fenceFd = mCurrentFence->dup(); - if (fenceFd == -1) { - ST_LOGE("doGLFenceWait: error dup'ing fence fd: %d", errno); - return -errno; - } - EGLint attribs[] = { - EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, - EGL_NONE - }; - EGLSyncKHR sync = eglCreateSyncKHR(dpy, - EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); - if (sync == EGL_NO_SYNC_KHR) { - close(fenceFd); - ST_LOGE("doGLFenceWait: error creating EGL fence: %#x", - eglGetError()); - return UNKNOWN_ERROR; - } - - // XXX: The spec draft is inconsistent as to whether this should - // return an EGLint or void. Ignore the return value for now, as - // it's not strictly needed. - eglWaitSyncANDROID(dpy, sync, 0); - EGLint eglErr = eglGetError(); - eglDestroySyncKHR(dpy, sync); - if (eglErr != EGL_SUCCESS) { - ST_LOGE("doGLFenceWait: error waiting for EGL fence: %#x", - eglErr); - return UNKNOWN_ERROR; - } - } else { - status_t err = mCurrentFence->waitForever(1000, - "SurfaceTexture::doGLFenceWaitLocked"); - if (err != NO_ERROR) { - ST_LOGE("doGLFenceWait: error waiting for fence: %d", err); - return err; - } - } - } - - return NO_ERROR; -} - -bool SurfaceTexture::isSynchronousMode() const { - Mutex::Autolock lock(mMutex); - return mBufferQueue->isSynchronousMode(); -} - -void SurfaceTexture::freeBufferLocked(int slotIndex) { - ST_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); - if (slotIndex == mCurrentTexture) { - mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; - } - 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; - ConsumerBase::freeBufferLocked(slotIndex); -} - -void SurfaceTexture::abandonLocked() { - ST_LOGV("abandonLocked"); - mCurrentTextureBuf.clear(); - ConsumerBase::abandonLocked(); -} - -void SurfaceTexture::setName(const String8& name) { - Mutex::Autolock _l(mMutex); - mName = name; - mBufferQueue->setConsumerName(name); -} - -status_t SurfaceTexture::setDefaultBufferFormat(uint32_t defaultFormat) { - Mutex::Autolock lock(mMutex); - return mBufferQueue->setDefaultBufferFormat(defaultFormat); -} - -status_t SurfaceTexture::setConsumerUsageBits(uint32_t usage) { - Mutex::Autolock lock(mMutex); - usage |= DEFAULT_USAGE_FLAGS; - return mBufferQueue->setConsumerUsageBits(usage); -} - -status_t SurfaceTexture::setTransformHint(uint32_t hint) { - Mutex::Autolock lock(mMutex); - return mBufferQueue->setTransformHint(hint); -} - -// Used for refactoring BufferQueue from SurfaceTexture -// Should not be in final interface once users of SurfaceTexture are clean up. -status_t SurfaceTexture::setSynchronousMode(bool enabled) { - Mutex::Autolock lock(mMutex); - return mBufferQueue->setSynchronousMode(enabled); -} - -void SurfaceTexture::dumpLocked(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); - result.append(buffer); - - ConsumerBase::dumpLocked(result, prefix, buffer, size); -} - -static void mtxMul(float out[16], const float a[16], const float b[16]) { - out[0] = a[0]*b[0] + a[4]*b[1] + a[8]*b[2] + a[12]*b[3]; - out[1] = a[1]*b[0] + a[5]*b[1] + a[9]*b[2] + a[13]*b[3]; - out[2] = a[2]*b[0] + a[6]*b[1] + a[10]*b[2] + a[14]*b[3]; - out[3] = a[3]*b[0] + a[7]*b[1] + a[11]*b[2] + a[15]*b[3]; - - out[4] = a[0]*b[4] + a[4]*b[5] + a[8]*b[6] + a[12]*b[7]; - out[5] = a[1]*b[4] + a[5]*b[5] + a[9]*b[6] + a[13]*b[7]; - out[6] = a[2]*b[4] + a[6]*b[5] + a[10]*b[6] + a[14]*b[7]; - out[7] = a[3]*b[4] + a[7]*b[5] + a[11]*b[6] + a[15]*b[7]; - - out[8] = a[0]*b[8] + a[4]*b[9] + a[8]*b[10] + a[12]*b[11]; - out[9] = a[1]*b[8] + a[5]*b[9] + a[9]*b[10] + a[13]*b[11]; - out[10] = a[2]*b[8] + a[6]*b[9] + a[10]*b[10] + a[14]*b[11]; - out[11] = a[3]*b[8] + a[7]*b[9] + a[11]*b[10] + a[15]*b[11]; - - out[12] = a[0]*b[12] + a[4]*b[13] + a[8]*b[14] + a[12]*b[15]; - out[13] = a[1]*b[12] + a[5]*b[13] + a[9]*b[14] + a[13]*b[15]; - out[14] = a[2]*b[12] + a[6]*b[13] + a[10]*b[14] + a[14]*b[15]; - out[15] = a[3]*b[12] + a[7]*b[13] + a[11]*b[14] + a[15]*b[15]; -} - -}; // namespace android diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp index 7588b9ecbe..c015b812f0 100644 --- a/libs/gui/SurfaceTextureClient.cpp +++ b/libs/gui/SurfaceTextureClient.cpp @@ -27,7 +27,7 @@ #include #include -#include +#include #include #include @@ -35,10 +35,10 @@ namespace android { SurfaceTextureClient::SurfaceTextureClient( - const sp& surfaceTexture) + const sp& bufferProducer) { SurfaceTextureClient::init(); - SurfaceTextureClient::setISurfaceTexture(surfaceTexture); + SurfaceTextureClient::setISurfaceTexture(bufferProducer); } SurfaceTextureClient::SurfaceTextureClient() { @@ -86,12 +86,12 @@ void SurfaceTextureClient::init() { } void SurfaceTextureClient::setISurfaceTexture( - const sp& surfaceTexture) + const sp& bufferProducer) { - mSurfaceTexture = surfaceTexture; + mSurfaceTexture = bufferProducer; } -sp SurfaceTextureClient::getISurfaceTexture() const { +sp SurfaceTextureClient::getISurfaceTexture() const { return mSurfaceTexture; } @@ -197,20 +197,20 @@ int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer, status_t result = mSurfaceTexture->dequeueBuffer(&buf, fence, reqW, reqH, mReqFormat, mReqUsage); if (result < 0) { - ALOGV("dequeueBuffer: ISurfaceTexture::dequeueBuffer(%d, %d, %d, %d)" + ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer(%d, %d, %d, %d)" "failed: %d", mReqWidth, mReqHeight, mReqFormat, mReqUsage, result); return result; } sp& gbuf(mSlots[buf].buffer); - if (result & ISurfaceTexture::RELEASE_ALL_BUFFERS) { + if (result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) { freeAllBuffers(); } - if ((result & ISurfaceTexture::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) { + if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) { result = mSurfaceTexture->requestBuffer(buf, &gbuf); if (result != NO_ERROR) { - ALOGE("dequeueBuffer: ISurfaceTexture::requestBuffer failed: %d", + ALOGE("dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: %d", result); return result; } @@ -288,8 +288,8 @@ int SurfaceTextureClient::queueBuffer(android_native_buffer_t* buffer, int fence mCrop.intersect(Rect(buffer->width, buffer->height), &crop); sp fence(fenceFd >= 0 ? new Fence(fenceFd) : NULL); - ISurfaceTexture::QueueBufferOutput output; - ISurfaceTexture::QueueBufferInput input(timestamp, crop, mScalingMode, + IGraphicBufferProducer::QueueBufferOutput output; + IGraphicBufferProducer::QueueBufferInput input(timestamp, crop, mScalingMode, mTransform, fence); status_t err = mSurfaceTexture->queueBuffer(i, input, &output); if (err != OK) { @@ -497,7 +497,7 @@ int SurfaceTextureClient::connect(int api) { ATRACE_CALL(); ALOGV("SurfaceTextureClient::connect"); Mutex::Autolock lock(mMutex); - ISurfaceTexture::QueueBufferOutput output; + IGraphicBufferProducer::QueueBufferOutput output; int err = mSurfaceTexture->connect(api, &output); if (err == NO_ERROR) { uint32_t numPendingBuffers = 0; @@ -566,7 +566,7 @@ int SurfaceTextureClient::setBufferCount(int bufferCount) Mutex::Autolock lock(mMutex); status_t err = mSurfaceTexture->setBufferCount(bufferCount); - ALOGE_IF(err, "ISurfaceTexture::setBufferCount(%d) returned %s", + ALOGE_IF(err, "IGraphicBufferProducer::setBufferCount(%d) returned %s", bufferCount, strerror(-err)); if (err == NO_ERROR) { diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 817abb4837..12cbfb0d7d 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -63,19 +63,19 @@ struct DummyConsumer : public BufferQueue::ConsumerListener { TEST_F(BufferQueueTest, AcquireBuffer_ExceedsMaxAcquireCount_Fails) { sp dc(new DummyConsumer); mBQ->consumerConnect(dc); - ISurfaceTexture::QueueBufferOutput qbo; + IGraphicBufferProducer::QueueBufferOutput qbo; mBQ->connect(NATIVE_WINDOW_API_CPU, &qbo); mBQ->setBufferCount(4); int slot; sp fence; sp buf; - ISurfaceTexture::QueueBufferInput qbi(0, Rect(0, 0, 1, 1), + IGraphicBufferProducer::QueueBufferInput qbi(0, Rect(0, 0, 1, 1), NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, fence); BufferQueue::BufferItem item; for (int i = 0; i < 2; i++) { - ASSERT_EQ(ISurfaceTexture::BUFFER_NEEDS_REALLOCATION, + ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mBQ->dequeueBuffer(&slot, fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN)); ASSERT_EQ(OK, mBQ->requestBuffer(slot, &buf)); @@ -83,7 +83,7 @@ TEST_F(BufferQueueTest, AcquireBuffer_ExceedsMaxAcquireCount_Fails) { ASSERT_EQ(OK, mBQ->acquireBuffer(&item)); } - ASSERT_EQ(ISurfaceTexture::BUFFER_NEEDS_REALLOCATION, + ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mBQ->dequeueBuffer(&slot, fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN)); ASSERT_EQ(OK, mBQ->requestBuffer(slot, &buf)); diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp index baeca0652d..2df83a08d0 100644 --- a/libs/gui/tests/SurfaceTextureClient_test.cpp +++ b/libs/gui/tests/SurfaceTextureClient_test.cpp @@ -40,7 +40,7 @@ protected: ALOGV("Begin test: %s.%s", testInfo->test_case_name(), testInfo->name()); - mST = new SurfaceTexture(123); + mST = new GLConsumer(123); mSTC = new SurfaceTextureClient(mST->getBufferQueue()); mANW = mSTC; @@ -102,7 +102,7 @@ protected: return sDefaultConfigAttribs; } - sp mST; + sp mST; sp mSTC; sp mANW; @@ -112,7 +112,7 @@ protected: }; TEST_F(SurfaceTextureClientTest, GetISurfaceTextureIsNotNull) { - sp ist(mSTC->getISurfaceTexture()); + sp ist(mSTC->getISurfaceTexture()); ASSERT_TRUE(ist != NULL); } @@ -250,7 +250,7 @@ TEST_F(SurfaceTextureClientTest, BufferGeometrySizeCanBeChangedWithoutFormat) { } TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSize) { - sp st(mST); + sp st(mST); ANativeWindowBuffer* buf; EXPECT_EQ(OK, st->setDefaultBufferSize(16, 8)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf)); @@ -464,7 +464,7 @@ TEST_F(SurfaceTextureClientTest, SetCropCropsCrop) { // from the SurfaceTexture class. TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeWaitRetire) { class MyThread : public Thread { - sp mST; + sp mST; EGLContext ctx; EGLSurface sur; EGLDisplay dpy; @@ -480,7 +480,7 @@ TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeWaitRetire) { return false; } public: - MyThread(const sp& mST) + MyThread(const sp& mST) : mST(mST), mBufferRetired(false) { ctx = eglGetCurrentContext(); sur = eglGetCurrentSurface(EGL_DRAW); @@ -685,7 +685,7 @@ protected: ASSERT_NE(EGL_NO_CONTEXT, mEglContext); for (int i = 0; i < NUM_SURFACE_TEXTURES; i++) { - sp st(new SurfaceTexture(i)); + sp st(new GLConsumer(i)); sp stc(new SurfaceTextureClient(st->getBufferQueue())); mEglSurfaces[i] = eglCreateWindowSurface(mEglDisplay, myConfig, static_cast(stc.get()), NULL); diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp index 58976adbaf..b6020ca2af 100644 --- a/libs/gui/tests/SurfaceTexture_test.cpp +++ b/libs/gui/tests/SurfaceTexture_test.cpp @@ -18,7 +18,7 @@ //#define LOG_NDEBUG 0 #include -#include +#include #include #include #include @@ -384,7 +384,7 @@ protected: virtual void SetUp() { GLTest::SetUp(); - mST = new SurfaceTexture(TEX_ID); + mST = new GLConsumer(TEX_ID); mSTC = new SurfaceTextureClient(mST->getBufferQueue()); mANW = mSTC; mTextureRenderer = new TextureRenderer(TEX_ID, mST); @@ -406,7 +406,7 @@ protected: class TextureRenderer: public RefBase { public: - TextureRenderer(GLuint texName, const sp& st): + TextureRenderer(GLuint texName, const sp& st): mTexName(texName), mST(st) { } @@ -447,7 +447,7 @@ protected: ASSERT_NE(-1, mTexMatrixHandle); } - // drawTexture draws the SurfaceTexture over the entire GL viewport. + // drawTexture draws the GLConsumer over the entire GL viewport. void drawTexture() { static const GLfloat triangleVertices[] = { -1.0f, 1.0f, @@ -494,14 +494,14 @@ protected: } GLuint mTexName; - sp mST; + sp mST; GLuint mPgm; GLint mPositionHandle; GLint mTexSamplerHandle; GLint mTexMatrixHandle; }; - class FrameWaiter : public SurfaceTexture::FrameAvailableListener { + class FrameWaiter : public GLConsumer::FrameAvailableListener { public: FrameWaiter(): mPendingFrames(0) { @@ -526,7 +526,7 @@ protected: Condition mCondition; }; - // Note that SurfaceTexture will lose the notifications + // Note that GLConsumer will lose the notifications // onBuffersReleased and onFrameAvailable as there is currently // no way to forward the events. This DisconnectWaiter will not let the // disconnect finish until finishDisconnect() is called. It will @@ -575,7 +575,7 @@ protected: Condition mFrameCondition; }; - sp mST; + sp mST; sp mSTC; sp mANW; sp mTextureRenderer; @@ -1070,7 +1070,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledRGBABufferPow2) { EXPECT_TRUE(checkPixel( 3, 52, 35, 231, 35, 35)); } -// Tests if SurfaceTexture and BufferQueue are robust enough +// Tests if GLConsumer and BufferQueue are robust enough // to handle a special case where updateTexImage is called // in the middle of disconnect. This ordering is enforced // by blocking in the disconnect callback. @@ -1123,12 +1123,12 @@ TEST_F(SurfaceTextureGLTest, DisconnectStressTest) { sp pt(new ProducerThread(mANW)); pt->run(); - // eat a frame so SurfaceTexture will own an at least one slot + // eat a frame so GLConsumer will own an at least one slot dw->waitForFrame(); EXPECT_EQ(OK,mST->updateTexImage()); dw->waitForFrame(); - // Could fail here as SurfaceTexture thinks it still owns the slot + // Could fail here as GLConsumer thinks it still owns the slot // but bufferQueue has released all slots EXPECT_EQ(OK,mST->updateTexImage()); @@ -1136,7 +1136,7 @@ TEST_F(SurfaceTextureGLTest, DisconnectStressTest) { } -// This test ensures that the SurfaceTexture clears the mCurrentTexture +// This test ensures that the GLConsumer clears the mCurrentTexture // when it is disconnected and reconnected. Otherwise it will // attempt to release a buffer that it does not owned TEST_F(SurfaceTextureGLTest, DisconnectClearsCurrentTexture) { @@ -1581,7 +1581,7 @@ TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceUnrefsBuffers) { // This test should have the only reference to buffer 0. EXPECT_EQ(1, buffers[0]->getStrongCount()); - // The SurfaceTexture should hold a single reference to buffer 1 in its + // The GLConsumer should hold a single reference to buffer 1 in its // mCurrentBuffer member. All of the references in the slots should have // been released. EXPECT_EQ(2, buffers[1]->getStrongCount()); @@ -1615,7 +1615,7 @@ TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceAfterAbandonUnrefsBuffers) { buffers[i] = mST->getCurrentBuffer(); } - // Abandon the SurfaceTexture, releasing the ref that the SurfaceTexture has + // Abandon the GLConsumer, releasing the ref that the GLConsumer has // on buffers[2]. mST->abandon(); @@ -1847,7 +1847,7 @@ TEST_F(SurfaceTextureGLToGLTest, TexturingFromPreRotatedGLFilledBuffer) { * 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 + * GLConsumer. Additionally it supports interlocking the producer and * consumer threads so that a specific sequence of calls can be * deterministically created by the test. * @@ -1914,13 +1914,13 @@ protected: // FrameCondition is a utility class for interlocking between the producer // and consumer threads. The FrameCondition object should be created and // destroyed in the consumer thread only. The consumer thread should set - // the FrameCondition as the FrameAvailableListener of the SurfaceTexture, + // the FrameCondition as the FrameAvailableListener of the GLConsumer, // and should call both waitForFrame and finishFrame once for each expected // frame. // // This interlocking relies on the fact that onFrameAvailable gets called - // synchronously from SurfaceTexture::queueBuffer. - class FrameCondition : public SurfaceTexture::FrameAvailableListener { + // synchronously from GLConsumer::queueBuffer. + class FrameCondition : public GLConsumer::FrameAvailableListener { public: FrameCondition(): mFrameAvailable(false), @@ -1951,7 +1951,7 @@ protected: ALOGV("-finishFrame"); } - // This should be called by SurfaceTexture on the producer thread. + // This should be called by GLConsumer on the producer thread. virtual void onFrameAvailable() { Mutex::Autolock lock(mMutex); ALOGV("+onFrameAvailable"); -- cgit v1.2.3-59-g8ed1b