OSDN Git Service

SurfaceTexture: add the abandon method.
authorJamie Gennis <jgennis@google.com>
Tue, 19 Jul 2011 19:08:33 +0000 (12:08 -0700)
committerJamie Gennis <jgennis@google.com>
Fri, 22 Jul 2011 21:20:41 +0000 (14:20 -0700)
This change adds the 'abandon' method to the SurfaceTexture C++ class.
This method may be used to put the SurfaceTexture in an abandoned state,
causing all ISurfaceTexture methods to fail.

Change-Id: Ibd261f7b73f44e2bec36a8508bf92113cfb7cf95

include/gui/ISurfaceTexture.h
include/gui/SurfaceTexture.h
libs/gui/ISurfaceTexture.cpp
libs/gui/SurfaceTexture.cpp
libs/gui/SurfaceTextureClient.cpp
libs/gui/tests/SurfaceTexture_test.cpp

index 405a25a..1eda646 100644 (file)
@@ -51,7 +51,7 @@ protected:
     // the given slot index, and the client is expected to mirror the
     // slot->buffer mapping so that it's not necessary to transfer a
     // GraphicBuffer for every dequeue operation.
-    virtual sp<GraphicBuffer> requestBuffer(int slot) = 0;
+    virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) = 0;
 
     // setBufferCount sets the number of buffer slots available. Calling this
     // will also cause all buffer slots to be emptied. The caller should empty
index 62ea943..134c208 100644 (file)
@@ -69,7 +69,7 @@ public:
     // SurfaceTexture object (i.e. they are not owned by the client).
     virtual status_t setBufferCount(int bufferCount);
 
-    virtual sp<GraphicBuffer> requestBuffer(int buf);
+    virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
 
     // dequeueBuffer gets the next buffer slot index for the client to use. If a
     // buffer slot is available then that slot index is written to the location
@@ -190,6 +190,17 @@ public:
     // getCurrentScalingMode returns the scaling mode of the current buffer
     uint32_t getCurrentScalingMode() const;
 
+    // abandon frees all the buffers and puts the SurfaceTexture into the
+    // 'abandoned' state.  Once put in this state the SurfaceTexture can never
+    // leave it.  When in the 'abandoned' state, all methods of the
+    // ISurfaceTexture interface will fail with the NO_INIT error.
+    //
+    // Note that while calling this method causes all the buffers to be freed
+    // from the perspective of the the SurfaceTexture, if there are additional
+    // references on the buffers (e.g. if a buffer is referenced by a client or
+    // by OpenGL ES as a texture) then those buffer will remain allocated.
+    void abandon();
+
     // dump our state in a String
     void dump(String8& result) const;
     void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const;
@@ -411,6 +422,13 @@ private:
     typedef Vector<int> Fifo;
     Fifo mQueue;
 
+    // mAbandoned indicates that the SurfaceTexture will no longer be used to
+    // consume images buffers pushed to it using the ISurfaceTexture interface.
+    // It is initialized to false, and set to true in the abandon method.  A
+    // SurfaceTexture that has been abandoned will return the NO_INIT error from
+    // all ISurfaceTexture methods capable of returning an error.
+    bool mAbandoned;
+
     // mMutex is the mutex used to prevent concurrent access to the member
     // variables of SurfaceTexture objects. It must be locked whenever the
     // member variables are accessed.
index c9c7397..55246dc 100644 (file)
@@ -54,18 +54,18 @@ public:
     {
     }
 
-    virtual sp<GraphicBuffer> requestBuffer(int bufferIdx) {
+    virtual status_t requestBuffer(int bufferIdx, sp<GraphicBuffer>* buf) {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
         data.writeInt32(bufferIdx);
         remote()->transact(REQUEST_BUFFER, data, &reply);
-        sp<GraphicBuffer> buffer;
         bool nonNull = reply.readInt32();
         if (nonNull) {
-            buffer = new GraphicBuffer();
-            reply.read(*buffer);
+            *buf = new GraphicBuffer();
+            reply.read(**buf);
         }
-        return buffer;
+        status_t result = reply.readInt32();
+        return result;
     }
 
     virtual status_t setBufferCount(int bufferCount)
@@ -192,11 +192,13 @@ status_t BnSurfaceTexture::onTransact(
         case REQUEST_BUFFER: {
             CHECK_INTERFACE(ISurfaceTexture, data, reply);
             int bufferIdx   = data.readInt32();
-            sp<GraphicBuffer> buffer(requestBuffer(bufferIdx));
+            sp<GraphicBuffer> 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: {
index 54d963f..c190195 100644 (file)
@@ -94,7 +94,8 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode) :
     mTexName(tex),
     mSynchronousMode(false),
     mAllowSynchronousMode(allowSynchronousMode),
-    mConnectedApi(NO_CONNECTED_API) {
+    mConnectedApi(NO_CONNECTED_API),
+    mAbandoned(false) {
     LOGV("SurfaceTexture::SurfaceTexture");
     sp<ISurfaceComposer> composer(ComposerService::getComposerService());
     mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
@@ -150,6 +151,11 @@ status_t SurfaceTexture::setBufferCount(int bufferCount) {
     LOGV("SurfaceTexture::setBufferCount");
     Mutex::Autolock lock(mMutex);
 
+    if (mAbandoned) {
+        LOGE("setBufferCount: SurfaceTexture has been abandoned!");
+        return NO_INIT;
+    }
+
     if (bufferCount > NUM_BUFFER_SLOTS) {
         LOGE("setBufferCount: bufferCount larger than slots available");
         return BAD_VALUE;
@@ -199,22 +205,32 @@ status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h)
     return OK;
 }
 
-sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf) {
+status_t SurfaceTexture::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
     LOGV("SurfaceTexture::requestBuffer");
     Mutex::Autolock lock(mMutex);
-    if (buf < 0 || mBufferCount <= buf) {
+    if (mAbandoned) {
+        LOGE("requestBuffer: SurfaceTexture has been abandoned!");
+        return NO_INIT;
+    }
+    if (slot < 0 || mBufferCount <= slot) {
         LOGE("requestBuffer: slot index out of range [0, %d]: %d",
-                mBufferCount, buf);
-        return 0;
+                mBufferCount, slot);
+        return BAD_VALUE;
     }
-    mSlots[buf].mRequestBufferCalled = true;
-    return mSlots[buf].mGraphicBuffer;
+    mSlots[slot].mRequestBufferCalled = true;
+    *buf = mSlots[slot].mGraphicBuffer;
+    return NO_ERROR;
 }
 
 status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
         uint32_t format, uint32_t usage) {
     LOGV("SurfaceTexture::dequeueBuffer");
 
+    if (mAbandoned) {
+        LOGE("dequeueBuffer: SurfaceTexture has been abandoned!");
+        return NO_INIT;
+    }
+
     if ((w && !h) || (!w && h)) {
         LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h);
         return BAD_VALUE;
@@ -252,6 +268,11 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
             // wait for the FIFO to drain
             while (!mQueue.isEmpty()) {
                 mDequeueCondition.wait(mMutex);
+                if (mAbandoned) {
+                    LOGE("dequeueBuffer: SurfaceTexture was abandoned while "
+                            "blocked!");
+                    return NO_INIT;
+                }
             }
             minBufferCountNeeded = mSynchronousMode ?
                     MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
@@ -380,6 +401,11 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
 status_t SurfaceTexture::setSynchronousMode(bool enabled) {
     Mutex::Autolock lock(mMutex);
 
+    if (mAbandoned) {
+        LOGE("setSynchronousMode: SurfaceTexture has been abandoned!");
+        return NO_INIT;
+    }
+
     status_t err = OK;
     if (!mAllowSynchronousMode && enabled)
         return err;
@@ -410,6 +436,10 @@ status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp,
 
     { // scope for the lock
         Mutex::Autolock lock(mMutex);
+        if (mAbandoned) {
+            LOGE("queueBuffer: SurfaceTexture has been abandoned!");
+            return NO_INIT;
+        }
         if (buf < 0 || buf >= mBufferCount) {
             LOGE("queueBuffer: slot index out of range [0, %d]: %d",
                     mBufferCount, buf);
@@ -475,6 +505,12 @@ status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp,
 void SurfaceTexture::cancelBuffer(int buf) {
     LOGV("SurfaceTexture::cancelBuffer");
     Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        LOGW("cancelBuffer: SurfaceTexture has been abandoned!");
+        return;
+    }
+
     if (buf < 0 || buf >= mBufferCount) {
         LOGE("cancelBuffer: slot index out of range [0, %d]: %d",
                 mBufferCount, buf);
@@ -491,6 +527,10 @@ void SurfaceTexture::cancelBuffer(int buf) {
 status_t SurfaceTexture::setCrop(const Rect& crop) {
     LOGV("SurfaceTexture::setCrop");
     Mutex::Autolock lock(mMutex);
+    if (mAbandoned) {
+        LOGE("setCrop: SurfaceTexture has been abandoned!");
+        return NO_INIT;
+    }
     mNextCrop = crop;
     return OK;
 }
@@ -498,6 +538,10 @@ status_t SurfaceTexture::setCrop(const Rect& crop) {
 status_t SurfaceTexture::setTransform(uint32_t transform) {
     LOGV("SurfaceTexture::setTransform");
     Mutex::Autolock lock(mMutex);
+    if (mAbandoned) {
+        LOGE("setTransform: SurfaceTexture has been abandoned!");
+        return NO_INIT;
+    }
     mNextTransform = transform;
     return OK;
 }
@@ -505,6 +549,12 @@ status_t SurfaceTexture::setTransform(uint32_t transform) {
 status_t SurfaceTexture::connect(int api) {
     LOGV("SurfaceTexture::connect(this=%p, %d)", this, api);
     Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        LOGE("connect: SurfaceTexture has been abandoned!");
+        return NO_INIT;
+    }
+
     int err = NO_ERROR;
     switch (api) {
         case NATIVE_WINDOW_API_EGL:
@@ -529,6 +579,12 @@ status_t SurfaceTexture::connect(int api) {
 status_t SurfaceTexture::disconnect(int api) {
     LOGV("SurfaceTexture::disconnect(this=%p, %d)", this, api);
     Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        LOGE("connect: SurfaceTexture has been abandoned!");
+        return NO_INIT;
+    }
+
     int err = NO_ERROR;
     switch (api) {
         case NATIVE_WINDOW_API_EGL:
@@ -837,6 +893,12 @@ uint32_t SurfaceTexture::getCurrentScalingMode() const {
 int SurfaceTexture::query(int what, int* outValue)
 {
     Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        LOGE("query: SurfaceTexture has been abandoned!");
+        return NO_INIT;
+    }
+
     int value;
     switch (what) {
     case NATIVE_WINDOW_WIDTH:
@@ -863,6 +925,13 @@ int SurfaceTexture::query(int what, int* outValue)
     return NO_ERROR;
 }
 
+void SurfaceTexture::abandon() {
+    Mutex::Autolock lock(mMutex);
+    freeAllBuffers();
+    mAbandoned = true;
+    mDequeueCondition.signal();
+}
+
 void SurfaceTexture::dump(String8& result) const
 {
     char buffer[1024];
index 688b99b..df0ad5a 100644 (file)
@@ -148,10 +148,11 @@ int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer) {
     }
 
     if ((result & ISurfaceTexture::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
-        gbuf = mSurfaceTexture->requestBuffer(buf);
-        if (gbuf == 0) {
-            LOGE("dequeueBuffer: ISurfaceTexture::requestBuffer failed");
-            return NO_MEMORY;
+        result = mSurfaceTexture->requestBuffer(buf, &gbuf);
+        if (result != NO_ERROR) {
+            LOGE("dequeueBuffer: ISurfaceTexture::requestBuffer failed: %d",
+                    result);
+            return result;
         }
         mQueryWidth  = gbuf->width;
         mQueryHeight = gbuf->height;
index 2b55e83..0fac6cd 100644 (file)
@@ -1018,6 +1018,83 @@ TEST_F(SurfaceTextureGLTest, DISABLED_TexturingFromGLFilledRGBABufferPow2) {
     EXPECT_TRUE(checkPixel( 3, 52, 153, 153, 153, 153));
 }
 
+TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) {
+    class ProducerThread : public Thread {
+    public:
+        ProducerThread(const sp<ANativeWindow>& anw):
+                mANW(anw),
+                mDequeueError(NO_ERROR) {
+        }
+
+        virtual ~ProducerThread() {
+        }
+
+        virtual bool threadLoop() {
+            Mutex::Autolock lock(mMutex);
+            ANativeWindowBuffer* anb;
+
+            // Frame 1
+            if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) {
+                return false;
+            }
+            if (anb == NULL) {
+                return false;
+            }
+            if (mANW->queueBuffer(mANW.get(), anb)
+                    != NO_ERROR) {
+                return false;
+            }
+
+            // Frame 2
+            if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) {
+                return false;
+            }
+            if (anb == NULL) {
+                return false;
+            }
+            if (mANW->queueBuffer(mANW.get(), anb)
+                    != NO_ERROR) {
+                return false;
+            }
+
+            // Frame 3 - error expected
+            mDequeueError = mANW->dequeueBuffer(mANW.get(), &anb);
+            return false;
+        }
+
+        status_t getDequeueError() {
+            Mutex::Autolock lock(mMutex);
+            return mDequeueError;
+        }
+
+    private:
+        sp<ANativeWindow> mANW;
+        status_t mDequeueError;
+        Mutex mMutex;
+    };
+
+    sp<FrameWaiter> fw(new FrameWaiter);
+    mST->setFrameAvailableListener(fw);
+    ASSERT_EQ(OK, mST->setSynchronousMode(true));
+    ASSERT_EQ(OK, mST->setBufferCountServer(2));
+
+    sp<Thread> pt(new ProducerThread(mANW));
+    pt->run();
+
+    fw->waitForFrame();
+    fw->waitForFrame();
+
+    // Sleep for 100ms to allow the producer thread's dequeueBuffer call to
+    // block waiting for a buffer to become available.
+    usleep(100000);
+
+    mST->abandon();
+
+    pt->requestExitAndWait();
+    ASSERT_EQ(NO_INIT,
+            reinterpret_cast<ProducerThread*>(pt.get())->getDequeueError());
+}
+
 /*
  * This test is for testing GL -> GL texture streaming via SurfaceTexture.  It
  * contains functionality to create a producer thread that will perform GL