SurfaceTexture: add the abandon method.

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
This commit is contained in:
Jamie Gennis 2011-07-19 12:08:33 -07:00
parent 6e50219aee
commit 7b305fffc3
6 changed files with 187 additions and 20 deletions

View File

@ -51,7 +51,7 @@ protected:
// the given slot index, and the client is expected to mirror the // the given slot index, and the client is expected to mirror the
// slot->buffer mapping so that it's not necessary to transfer a // slot->buffer mapping so that it's not necessary to transfer a
// GraphicBuffer for every dequeue operation. // 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 // setBufferCount sets the number of buffer slots available. Calling this
// will also cause all buffer slots to be emptied. The caller should empty // will also cause all buffer slots to be emptied. The caller should empty

View File

@ -69,7 +69,7 @@ public:
// SurfaceTexture object (i.e. they are not owned by the client). // SurfaceTexture object (i.e. they are not owned by the client).
virtual status_t setBufferCount(int bufferCount); 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 // 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 // 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 // getCurrentScalingMode returns the scaling mode of the current buffer
uint32_t getCurrentScalingMode() const; 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 // dump our state in a String
void dump(String8& result) const; void dump(String8& result) const;
void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const; void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const;
@ -411,6 +422,13 @@ private:
typedef Vector<int> Fifo; typedef Vector<int> Fifo;
Fifo mQueue; 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 // mMutex is the mutex used to prevent concurrent access to the member
// variables of SurfaceTexture objects. It must be locked whenever the // variables of SurfaceTexture objects. It must be locked whenever the
// member variables are accessed. // member variables are accessed.

View File

@ -54,18 +54,18 @@ public:
{ {
} }
virtual sp<GraphicBuffer> requestBuffer(int bufferIdx) { virtual status_t requestBuffer(int bufferIdx, sp<GraphicBuffer>* buf) {
Parcel data, reply; Parcel data, reply;
data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
data.writeInt32(bufferIdx); data.writeInt32(bufferIdx);
remote()->transact(REQUEST_BUFFER, data, &reply); remote()->transact(REQUEST_BUFFER, data, &reply);
sp<GraphicBuffer> buffer;
bool nonNull = reply.readInt32(); bool nonNull = reply.readInt32();
if (nonNull) { if (nonNull) {
buffer = new GraphicBuffer(); *buf = new GraphicBuffer();
reply.read(*buffer); reply.read(**buf);
} }
return buffer; status_t result = reply.readInt32();
return result;
} }
virtual status_t setBufferCount(int bufferCount) virtual status_t setBufferCount(int bufferCount)
@ -192,11 +192,13 @@ status_t BnSurfaceTexture::onTransact(
case REQUEST_BUFFER: { case REQUEST_BUFFER: {
CHECK_INTERFACE(ISurfaceTexture, data, reply); CHECK_INTERFACE(ISurfaceTexture, data, reply);
int bufferIdx = data.readInt32(); int bufferIdx = data.readInt32();
sp<GraphicBuffer> buffer(requestBuffer(bufferIdx)); sp<GraphicBuffer> buffer;
int result = requestBuffer(bufferIdx, &buffer);
reply->writeInt32(buffer != 0); reply->writeInt32(buffer != 0);
if (buffer != 0) { if (buffer != 0) {
reply->write(*buffer); reply->write(*buffer);
} }
reply->writeInt32(result);
return NO_ERROR; return NO_ERROR;
} break; } break;
case SET_BUFFER_COUNT: { case SET_BUFFER_COUNT: {

View File

@ -94,7 +94,8 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode) :
mTexName(tex), mTexName(tex),
mSynchronousMode(false), mSynchronousMode(false),
mAllowSynchronousMode(allowSynchronousMode), mAllowSynchronousMode(allowSynchronousMode),
mConnectedApi(NO_CONNECTED_API) { mConnectedApi(NO_CONNECTED_API),
mAbandoned(false) {
LOGV("SurfaceTexture::SurfaceTexture"); LOGV("SurfaceTexture::SurfaceTexture");
sp<ISurfaceComposer> composer(ComposerService::getComposerService()); sp<ISurfaceComposer> composer(ComposerService::getComposerService());
mGraphicBufferAlloc = composer->createGraphicBufferAlloc(); mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
@ -150,6 +151,11 @@ status_t SurfaceTexture::setBufferCount(int bufferCount) {
LOGV("SurfaceTexture::setBufferCount"); LOGV("SurfaceTexture::setBufferCount");
Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex);
if (mAbandoned) {
LOGE("setBufferCount: SurfaceTexture has been abandoned!");
return NO_INIT;
}
if (bufferCount > NUM_BUFFER_SLOTS) { if (bufferCount > NUM_BUFFER_SLOTS) {
LOGE("setBufferCount: bufferCount larger than slots available"); LOGE("setBufferCount: bufferCount larger than slots available");
return BAD_VALUE; return BAD_VALUE;
@ -199,22 +205,32 @@ status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h)
return OK; return OK;
} }
sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf) { status_t SurfaceTexture::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
LOGV("SurfaceTexture::requestBuffer"); LOGV("SurfaceTexture::requestBuffer");
Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex);
if (buf < 0 || mBufferCount <= buf) { if (mAbandoned) {
LOGE("requestBuffer: slot index out of range [0, %d]: %d", LOGE("requestBuffer: SurfaceTexture has been abandoned!");
mBufferCount, buf); return NO_INIT;
return 0;
} }
mSlots[buf].mRequestBufferCalled = true; if (slot < 0 || mBufferCount <= slot) {
return mSlots[buf].mGraphicBuffer; LOGE("requestBuffer: slot index out of range [0, %d]: %d",
mBufferCount, slot);
return BAD_VALUE;
}
mSlots[slot].mRequestBufferCalled = true;
*buf = mSlots[slot].mGraphicBuffer;
return NO_ERROR;
} }
status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
uint32_t format, uint32_t usage) { uint32_t format, uint32_t usage) {
LOGV("SurfaceTexture::dequeueBuffer"); LOGV("SurfaceTexture::dequeueBuffer");
if (mAbandoned) {
LOGE("dequeueBuffer: SurfaceTexture has been abandoned!");
return NO_INIT;
}
if ((w && !h) || (!w && h)) { if ((w && !h) || (!w && h)) {
LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h); LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h);
return BAD_VALUE; 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 // wait for the FIFO to drain
while (!mQueue.isEmpty()) { while (!mQueue.isEmpty()) {
mDequeueCondition.wait(mMutex); mDequeueCondition.wait(mMutex);
if (mAbandoned) {
LOGE("dequeueBuffer: SurfaceTexture was abandoned while "
"blocked!");
return NO_INIT;
}
} }
minBufferCountNeeded = mSynchronousMode ? minBufferCountNeeded = mSynchronousMode ?
MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; 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) { status_t SurfaceTexture::setSynchronousMode(bool enabled) {
Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex);
if (mAbandoned) {
LOGE("setSynchronousMode: SurfaceTexture has been abandoned!");
return NO_INIT;
}
status_t err = OK; status_t err = OK;
if (!mAllowSynchronousMode && enabled) if (!mAllowSynchronousMode && enabled)
return err; return err;
@ -410,6 +436,10 @@ status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp,
{ // scope for the lock { // scope for the lock
Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex);
if (mAbandoned) {
LOGE("queueBuffer: SurfaceTexture has been abandoned!");
return NO_INIT;
}
if (buf < 0 || buf >= mBufferCount) { if (buf < 0 || buf >= mBufferCount) {
LOGE("queueBuffer: slot index out of range [0, %d]: %d", LOGE("queueBuffer: slot index out of range [0, %d]: %d",
mBufferCount, buf); mBufferCount, buf);
@ -475,6 +505,12 @@ status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp,
void SurfaceTexture::cancelBuffer(int buf) { void SurfaceTexture::cancelBuffer(int buf) {
LOGV("SurfaceTexture::cancelBuffer"); LOGV("SurfaceTexture::cancelBuffer");
Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex);
if (mAbandoned) {
LOGW("cancelBuffer: SurfaceTexture has been abandoned!");
return;
}
if (buf < 0 || buf >= mBufferCount) { if (buf < 0 || buf >= mBufferCount) {
LOGE("cancelBuffer: slot index out of range [0, %d]: %d", LOGE("cancelBuffer: slot index out of range [0, %d]: %d",
mBufferCount, buf); mBufferCount, buf);
@ -491,6 +527,10 @@ void SurfaceTexture::cancelBuffer(int buf) {
status_t SurfaceTexture::setCrop(const Rect& crop) { status_t SurfaceTexture::setCrop(const Rect& crop) {
LOGV("SurfaceTexture::setCrop"); LOGV("SurfaceTexture::setCrop");
Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex);
if (mAbandoned) {
LOGE("setCrop: SurfaceTexture has been abandoned!");
return NO_INIT;
}
mNextCrop = crop; mNextCrop = crop;
return OK; return OK;
} }
@ -498,6 +538,10 @@ status_t SurfaceTexture::setCrop(const Rect& crop) {
status_t SurfaceTexture::setTransform(uint32_t transform) { status_t SurfaceTexture::setTransform(uint32_t transform) {
LOGV("SurfaceTexture::setTransform"); LOGV("SurfaceTexture::setTransform");
Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex);
if (mAbandoned) {
LOGE("setTransform: SurfaceTexture has been abandoned!");
return NO_INIT;
}
mNextTransform = transform; mNextTransform = transform;
return OK; return OK;
} }
@ -505,6 +549,12 @@ status_t SurfaceTexture::setTransform(uint32_t transform) {
status_t SurfaceTexture::connect(int api) { status_t SurfaceTexture::connect(int api) {
LOGV("SurfaceTexture::connect(this=%p, %d)", this, api); LOGV("SurfaceTexture::connect(this=%p, %d)", this, api);
Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex);
if (mAbandoned) {
LOGE("connect: SurfaceTexture has been abandoned!");
return NO_INIT;
}
int err = NO_ERROR; int err = NO_ERROR;
switch (api) { switch (api) {
case NATIVE_WINDOW_API_EGL: case NATIVE_WINDOW_API_EGL:
@ -529,6 +579,12 @@ status_t SurfaceTexture::connect(int api) {
status_t SurfaceTexture::disconnect(int api) { status_t SurfaceTexture::disconnect(int api) {
LOGV("SurfaceTexture::disconnect(this=%p, %d)", this, api); LOGV("SurfaceTexture::disconnect(this=%p, %d)", this, api);
Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex);
if (mAbandoned) {
LOGE("connect: SurfaceTexture has been abandoned!");
return NO_INIT;
}
int err = NO_ERROR; int err = NO_ERROR;
switch (api) { switch (api) {
case NATIVE_WINDOW_API_EGL: case NATIVE_WINDOW_API_EGL:
@ -837,6 +893,12 @@ uint32_t SurfaceTexture::getCurrentScalingMode() const {
int SurfaceTexture::query(int what, int* outValue) int SurfaceTexture::query(int what, int* outValue)
{ {
Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex);
if (mAbandoned) {
LOGE("query: SurfaceTexture has been abandoned!");
return NO_INIT;
}
int value; int value;
switch (what) { switch (what) {
case NATIVE_WINDOW_WIDTH: case NATIVE_WINDOW_WIDTH:
@ -863,6 +925,13 @@ int SurfaceTexture::query(int what, int* outValue)
return NO_ERROR; return NO_ERROR;
} }
void SurfaceTexture::abandon() {
Mutex::Autolock lock(mMutex);
freeAllBuffers();
mAbandoned = true;
mDequeueCondition.signal();
}
void SurfaceTexture::dump(String8& result) const void SurfaceTexture::dump(String8& result) const
{ {
char buffer[1024]; char buffer[1024];

View File

@ -148,10 +148,11 @@ int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer) {
} }
if ((result & ISurfaceTexture::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) { if ((result & ISurfaceTexture::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
gbuf = mSurfaceTexture->requestBuffer(buf); result = mSurfaceTexture->requestBuffer(buf, &gbuf);
if (gbuf == 0) { if (result != NO_ERROR) {
LOGE("dequeueBuffer: ISurfaceTexture::requestBuffer failed"); LOGE("dequeueBuffer: ISurfaceTexture::requestBuffer failed: %d",
return NO_MEMORY; result);
return result;
} }
mQueryWidth = gbuf->width; mQueryWidth = gbuf->width;
mQueryHeight = gbuf->height; mQueryHeight = gbuf->height;

View File

@ -1018,6 +1018,83 @@ TEST_F(SurfaceTextureGLTest, DISABLED_TexturingFromGLFilledRGBABufferPow2) {
EXPECT_TRUE(checkPixel( 3, 52, 153, 153, 153, 153)); 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 * This test is for testing GL -> GL texture streaming via SurfaceTexture. It
* contains functionality to create a producer thread that will perform GL * contains functionality to create a producer thread that will perform GL