summaryrefslogtreecommitdiffstats
path: root/libs
diff options
context:
space:
mode:
authorMathias Agopian <mathias@google.com>2011-05-02 19:51:12 -0700
committerMathias Agopian <mathias@google.com>2011-05-11 18:01:51 -0700
commit402ff24aa199a2587498b694e2be67ceb1265c69 (patch)
tree90d37bedbae4aaf34029c23cd7569fe7bde27ae9 /libs
parenta6b717b52a79c83e66247ab49050ddf07b4d5126 (diff)
downloadframeworks_base-402ff24aa199a2587498b694e2be67ceb1265c69.zip
frameworks_base-402ff24aa199a2587498b694e2be67ceb1265c69.tar.gz
frameworks_base-402ff24aa199a2587498b694e2be67ceb1265c69.tar.bz2
Implement {Surface|SurfaceTextureClient}::setSwapInterval()
Change-Id: I8382e346ddaa2c4c8ff56ac3ffd7f0109572f188
Diffstat (limited to 'libs')
-rw-r--r--libs/gui/ISurfaceTexture.cpp18
-rw-r--r--libs/gui/SurfaceTexture.cpp174
-rw-r--r--libs/gui/SurfaceTextureClient.cpp32
-rw-r--r--libs/gui/tests/SurfaceTextureClient_test.cpp17
4 files changed, 202 insertions, 39 deletions
diff --git a/libs/gui/ISurfaceTexture.cpp b/libs/gui/ISurfaceTexture.cpp
index 0bd0f97..16e3780 100644
--- a/libs/gui/ISurfaceTexture.cpp
+++ b/libs/gui/ISurfaceTexture.cpp
@@ -40,6 +40,7 @@ enum {
SET_TRANSFORM,
GET_ALLOCATOR,
QUERY,
+ SET_SYNCHRONOUS_MODE,
};
@@ -144,6 +145,16 @@ public:
return result;
}
+ virtual status_t setSynchronousMode(bool enabled) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+ data.writeInt32(enabled);
+ remote()->transact(SET_SYNCHRONOUS_MODE, data, &reply);
+ status_t result = reply.readInt32();
+ return result;
+ }
+
+
};
IMPLEMENT_META_INTERFACE(SurfaceTexture, "android.gui.SurfaceTexture");
@@ -230,6 +241,13 @@ status_t BnSurfaceTexture::onTransact(
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;
}
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index adb468c..d8821e2 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -81,7 +81,9 @@ SurfaceTexture::SurfaceTexture(GLuint tex) :
mDefaultWidth(1),
mDefaultHeight(1),
mPixelFormat(PIXEL_FORMAT_RGBA_8888),
- mBufferCount(MIN_BUFFER_SLOTS),
+ mBufferCount(MIN_ASYNC_BUFFER_SLOTS),
+ mClientBufferCount(0),
+ mServerBufferCount(MIN_ASYNC_BUFFER_SLOTS),
mCurrentTexture(INVALID_BUFFER_SLOT),
mCurrentTextureTarget(GL_TEXTURE_EXTERNAL_OES),
mCurrentTransform(0),
@@ -100,22 +102,79 @@ SurfaceTexture::~SurfaceTexture() {
freeAllBuffers();
}
+status_t SurfaceTexture::setBufferCountServerLocked(int bufferCount) {
+ if (bufferCount > NUM_BUFFER_SLOTS)
+ return BAD_VALUE;
+
+ // special-case, nothing to do
+ if (bufferCount == mBufferCount)
+ return OK;
+
+ if (!mClientBufferCount &&
+ bufferCount >= mBufferCount) {
+ // easy, we just have more buffers
+ mBufferCount = bufferCount;
+ mServerBufferCount = bufferCount;
+ mDequeueCondition.signal();
+ } else {
+ // we're here because we're either
+ // - reducing the number of available buffers
+ // - or there is a client-buffer-count in effect
+
+ // less than 2 buffers is never allowed
+ if (bufferCount < 2)
+ return BAD_VALUE;
+
+ // when there is non client-buffer-count in effect, the client is not
+ // allowed to dequeue more than one buffer at a time,
+ // so the next time they dequeue a buffer, we know that they don't
+ // own one. the actual resizing will happen during the next
+ // dequeueBuffer.
+
+ mServerBufferCount = bufferCount;
+ }
+ return OK;
+}
+
+status_t SurfaceTexture::setBufferCountServer(int bufferCount) {
+ Mutex::Autolock lock(mMutex);
+ return setBufferCountServerLocked(bufferCount);
+}
+
status_t SurfaceTexture::setBufferCount(int bufferCount) {
LOGV("SurfaceTexture::setBufferCount");
Mutex::Autolock lock(mMutex);
- const int minBufferSlots = mSynchronousMode ?
- MIN_BUFFER_SLOTS-1 : MIN_BUFFER_SLOTS;
+ // Error out if the user has dequeued buffers
+ for (int i=0 ; i<mBufferCount ; i++) {
+ if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) {
+ LOGE("setBufferCount: client owns some buffers");
+ return -EINVAL;
+ }
+ }
+
+ if (bufferCount == 0) {
+ const int minBufferSlots = mSynchronousMode ?
+ MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
+ mClientBufferCount = 0;
+ bufferCount = (mServerBufferCount >= minBufferSlots) ?
+ mServerBufferCount : minBufferSlots;
+ return setBufferCountServerLocked(bufferCount);
+ }
- if (bufferCount < minBufferSlots) {
+ // We don't allow the client to set a buffer-count less than
+ // MIN_ASYNC_BUFFER_SLOTS (3), there is no reason for it.
+ if (bufferCount < MIN_ASYNC_BUFFER_SLOTS) {
return BAD_VALUE;
}
+ // here we're guaranteed that the client doesn't have dequeued buffers
+ // and will release all of its buffer references.
freeAllBuffers();
mBufferCount = bufferCount;
+ mClientBufferCount = bufferCount;
mCurrentTexture = INVALID_BUFFER_SLOT;
mQueue.clear();
- mQueue.reserve(mSynchronousMode ? mBufferCount : 1);
mDequeueCondition.signal();
return OK;
}
@@ -152,10 +211,56 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
}
Mutex::Autolock lock(mMutex);
+
+ status_t returnFlags(OK);
+
int found, foundSync;
int dequeuedCount = 0;
bool tryAgain = true;
while (tryAgain) {
+ // We need to wait for the FIFO to drain if the number of buffer
+ // needs to change.
+ //
+ // The condition "number of buffer needs to change" is true if
+ // - the client doesn't care about how many buffers there are
+ // - AND the actual number of buffer is different from what was
+ // set in the last setBufferCountServer()
+ // - OR -
+ // setBufferCountServer() was set to a value incompatible with
+ // the synchronization mode (for instance because the sync mode
+ // changed since)
+ //
+ // As long as this condition is true AND the FIFO is not empty, we
+ // wait on mDequeueCondition.
+
+ int minBufferCountNeeded = mSynchronousMode ?
+ MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
+
+ if (!mClientBufferCount &&
+ ((mServerBufferCount != mBufferCount) ||
+ (mServerBufferCount < minBufferCountNeeded))) {
+ // wait for the FIFO to drain
+ while (!mQueue.isEmpty()) {
+ mDequeueCondition.wait(mMutex);
+ }
+ minBufferCountNeeded = mSynchronousMode ?
+ MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
+ }
+
+
+ if (!mClientBufferCount &&
+ ((mServerBufferCount != mBufferCount) ||
+ (mServerBufferCount < minBufferCountNeeded))) {
+ // here we're guaranteed that mQueue is empty
+ freeAllBuffers();
+ mBufferCount = mServerBufferCount;
+ if (mBufferCount < minBufferCountNeeded)
+ mBufferCount = minBufferCountNeeded;
+ mCurrentTexture = INVALID_BUFFER_SLOT;
+ returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS;
+ }
+
+ // look for a free buffer to give to the client
found = INVALID_BUFFER_SLOT;
foundSync = INVALID_BUFFER_SLOT;
dequeuedCount = 0;
@@ -172,22 +277,34 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
}
}
}
+
+ // clients are not allowed to dequeue more than one buffer
+ // if they didn't set a buffer count.
+ if (!mClientBufferCount && dequeuedCount) {
+ return -EINVAL;
+ }
+
+ // make sure the client is not trying to dequeue more buffers
+ // than allowed.
+ const int avail = mBufferCount - (dequeuedCount+1);
+ if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) {
+ LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded (dequeued=%d)",
+ MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode),
+ dequeuedCount);
+ return -EBUSY;
+ }
+
// we're in synchronous mode and didn't find a buffer, we need to wait
+ // for for some buffers to be consumed
tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT);
if (tryAgain) {
mDequeueCondition.wait(mMutex);
}
}
- if (mSynchronousMode) {
- // we're dequeuing more buffers than allowed in synchronous mode
- if ((mBufferCount - (dequeuedCount+1)) < MIN_UNDEQUEUED_BUFFERS-1)
- return -EBUSY;
-
- if (found == INVALID_BUFFER_SLOT) {
- // foundSync guaranteed to be != INVALID_BUFFER_SLOT
- found = foundSync;
- }
+ if (mSynchronousMode && found == INVALID_BUFFER_SLOT) {
+ // foundSync guaranteed to be != INVALID_BUFFER_SLOT
+ found = foundSync;
}
if (found == INVALID_BUFFER_SLOT) {
@@ -238,22 +355,31 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
mSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
}
- return ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
+ returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
}
- return OK;
+ return returnFlags;
}
status_t SurfaceTexture::setSynchronousMode(bool enabled) {
Mutex::Autolock lock(mMutex);
+
+ status_t err = OK;
+ if (!enabled) {
+ // going to asynchronous mode, drain the queue
+ while (mSynchronousMode != enabled && !mQueue.isEmpty()) {
+ mDequeueCondition.wait(mMutex);
+ }
+ }
+
if (mSynchronousMode != enabled) {
+ // - if we're going to asynchronous mode, the queue is guaranteed to be
+ // empty here
+ // - if the client set the number of buffers, we're guaranteed that
+ // we have at least 3 (because we don't allow less)
mSynchronousMode = enabled;
- freeAllBuffers();
- mCurrentTexture = INVALID_BUFFER_SLOT;
- mQueue.clear();
- mQueue.reserve(mSynchronousMode ? mBufferCount : 1);
mDequeueCondition.signal();
}
- return NO_ERROR;
+ return err;
}
status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp) {
@@ -267,6 +393,9 @@ status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp) {
LOGE("queueBuffer: slot %d is not owned by the client (state=%d)",
buf, mSlots[buf].mBufferState);
return -EINVAL;
+ } else if (buf == mCurrentTexture) {
+ LOGE("queueBuffer: slot %d is current!", buf);
+ return -EINVAL;
} else if (!mSlots[buf].mRequestBufferCalled) {
LOGE("queueBuffer: slot %d was enqueued without requesting a buffer",
buf);
@@ -342,6 +471,9 @@ status_t SurfaceTexture::updateTexImage() {
Fifo::iterator front(mQueue.begin());
buf = *front;
mQueue.erase(front);
+ if (mQueue.isEmpty()) {
+ mDequeueCondition.signal();
+ }
}
// Initially both mCurrentTexture and buf are INVALID_BUFFER_SLOT,
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index f3ce44b..6f10320 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -39,6 +39,9 @@ SurfaceTextureClient::SurfaceTextureClient(
ANativeWindow::query = query;
ANativeWindow::perform = perform;
+ const_cast<int&>(ANativeWindow::minSwapInterval) = 0;
+ const_cast<int&>(ANativeWindow::maxSwapInterval) = 1;
+
// Get a reference to the allocator.
mAllocator = mSurfaceTexture->getAllocator();
}
@@ -90,22 +93,39 @@ int SurfaceTextureClient::perform(ANativeWindow* window, int operation, ...) {
}
int SurfaceTextureClient::setSwapInterval(int interval) {
- return INVALID_OPERATION;
+ // EGL specification states:
+ // interval is silently clamped to minimum and maximum implementation
+ // dependent values before being stored.
+ // Although we don't have to, we apply the same logic here.
+
+ if (interval < minSwapInterval)
+ interval = minSwapInterval;
+
+ if (interval > maxSwapInterval)
+ interval = maxSwapInterval;
+
+ status_t res = mSurfaceTexture->setSynchronousMode(interval ? true : false);
+
+ return res;
}
int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer) {
LOGV("SurfaceTextureClient::dequeueBuffer");
Mutex::Autolock lock(mMutex);
int buf = -1;
- status_t err = mSurfaceTexture->dequeueBuffer(&buf, mReqWidth, mReqHeight,
+ status_t result = mSurfaceTexture->dequeueBuffer(&buf, mReqWidth, mReqHeight,
mReqFormat, mReqUsage);
- if (err < 0) {
+ if (result < 0) {
LOGV("dequeueBuffer: ISurfaceTexture::dequeueBuffer(%d, %d, %d, %d)"
- "failed: %d", err, mReqWidth, mReqHeight, mReqFormat, mReqUsage);
- return err;
+ "failed: %d", result, mReqWidth, mReqHeight, mReqFormat, mReqUsage);
+ return result;
}
sp<GraphicBuffer>& gbuf(mSlots[buf]);
- if (err == ISurfaceTexture::BUFFER_NEEDS_REALLOCATION || gbuf == 0) {
+ if (result & ISurfaceTexture::RELEASE_ALL_BUFFERS) {
+ freeAllBuffers();
+ }
+
+ if ((result & ISurfaceTexture::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
gbuf = mSurfaceTexture->requestBuffer(buf);
if (gbuf == 0) {
LOGE("dequeueBuffer: ISurfaceTexture::requestBuffer failed");
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index e9c3411..59a4cc5 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -259,6 +259,7 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSizeAfterDequeue) {
sp<ANativeWindow> anw(mSTC);
sp<SurfaceTexture> st(mST);
ANativeWindowBuffer* buf[2];
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
EXPECT_NE(buf[0], buf[1]);
@@ -280,6 +281,7 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSizeVsGeometry) {
sp<ANativeWindow> anw(mSTC);
sp<SurfaceTexture> st(mST);
ANativeWindowBuffer* buf[2];
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
EXPECT_EQ(OK, st->setDefaultBufferSize(16, 8));
ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
@@ -307,7 +309,7 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureTooManyUpdateTexImage) {
sp<SurfaceTexture> st(mST);
android_native_buffer_t* buf[3];
ASSERT_EQ(OK, st->setSynchronousMode(false));
- ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3));
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
@@ -315,7 +317,7 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureTooManyUpdateTexImage) {
EXPECT_EQ(OK, st->updateTexImage());
ASSERT_EQ(OK, st->setSynchronousMode(true));
- ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 2));
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3));
ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
@@ -421,15 +423,6 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeDequeueCurrent) {
EXPECT_EQ(firstBuf, buf[2]);
}
-TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeTwoBuffers) {
- sp<ANativeWindow> anw(mSTC);
- sp<SurfaceTexture> st(mST);
- ASSERT_EQ(OK, st->setSynchronousMode(true));
- EXPECT_EQ(OK, native_window_set_buffer_count(anw.get(), 3));
- EXPECT_EQ(OK, native_window_set_buffer_count(anw.get(), 2));
- EXPECT_NE(OK, native_window_set_buffer_count(anw.get(), 1));
-}
-
TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeMinUndequeued) {
sp<ANativeWindow> anw(mSTC);
sp<SurfaceTexture> st(mST);
@@ -490,7 +483,7 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeWaitRetire) {
android_native_buffer_t* buf[3];
ASSERT_EQ(OK, st->setSynchronousMode(true));
- ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 2));
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3));
// dequeue/queue/update so we have a current buffer
ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));