SurfaceTexture can now force the client to request a buffer

SurfaceTexture now has the concept of default size a new method,
setDefaultBufferSize() to set it. When the default size is
changed, dequeueBuffer() will return a value telling the
client that it must ask for a new buffer.

The above only applies if the client has not
overriden the buffer size with setGeometry.

Change-Id: I520dc40363054b7e37fdb67d6a2e7bce70326e81
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index cdaca47..f4e2a67 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -76,6 +76,10 @@
 static void mtxMul(float out[16], const float a[16], const float b[16]);
 
 SurfaceTexture::SurfaceTexture(GLuint tex) :
+    mDefaultWidth(1),
+    mDefaultHeight(1),
+    mPixelFormat(PIXEL_FORMAT_RGBA_8888),
+    mUseDefaultSize(true),
     mBufferCount(MIN_BUFFER_SLOTS),
     mCurrentTexture(INVALID_BUFFER_SLOT),
     mCurrentTransform(0),
@@ -115,6 +119,16 @@
     return OK;
 }
 
+status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h)
+{
+    Mutex::Autolock lock(mMutex);
+    if ((w != mDefaultWidth) || (h != mDefaultHeight)) {
+        mDefaultWidth = w;
+        mDefaultHeight = h;
+    }
+    return OK;
+}
+
 sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf,
         uint32_t w, uint32_t h, uint32_t format, uint32_t usage) {
     LOGV("SurfaceTexture::requestBuffer");
@@ -124,12 +138,34 @@
                 mBufferCount, buf);
         return 0;
     }
+    if ((w && !h) || (!w & h)) {
+        LOGE("requestBuffer: invalid size: w=%u, h=%u: %d", w, h, buf);
+        return 0;
+    }
+
+    const bool useDefaultSize = !w && !h;
+    if (useDefaultSize) {
+        // use the default size
+        w = mDefaultWidth;
+        h = mDefaultHeight;
+    }
+
+    const bool updateFormat = (format != 0);
+    if (!updateFormat) {
+        // keep the current (or default) format
+        format = mPixelFormat;
+    }
+
     usage |= GraphicBuffer::USAGE_HW_TEXTURE;
     sp<GraphicBuffer> graphicBuffer(
             mGraphicBufferAlloc->createGraphicBuffer(w, h, format, usage));
     if (graphicBuffer == 0) {
         LOGE("requestBuffer: SurfaceComposer::createGraphicBuffer failed");
     } else {
+        mUseDefaultSize = useDefaultSize;
+        if (updateFormat) {
+            mPixelFormat = format;
+        }
         mSlots[buf].mGraphicBuffer = graphicBuffer;
         if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) {
             eglDestroyImageKHR(mSlots[buf].mEglDisplay, mSlots[buf].mEglImage);
@@ -155,7 +191,18 @@
     if (found == INVALID_BUFFER_SLOT) {
         return -EBUSY;
     }
+
     *buf = found;
+
+    const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
+    if (buffer == NULL) {
+        return ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
+    }
+    if ((mUseDefaultSize) &&
+        ((uint32_t(buffer->width) != mDefaultWidth) ||
+         (uint32_t(buffer->height) != mDefaultHeight))) {
+        return ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
+    }
     return OK;
 }
 
@@ -312,10 +359,10 @@
         } else {
             tx = 0.0f;
         }
-        if (mCurrentCrop.right < buf->getWidth()) {
+        if (mCurrentCrop.right < int32_t(buf->getWidth())) {
             xshrink++;
         }
-        if (mCurrentCrop.bottom < buf->getHeight()) {
+        if (mCurrentCrop.bottom < int32_t(buf->getHeight())) {
             ty = (float(buf->getHeight() - mCurrentCrop.bottom) + 1.0f) /
                     float(buf->getHeight());
             yshrink++;
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index a4812d0..29fc4d3 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -25,8 +25,8 @@
 
 SurfaceTextureClient::SurfaceTextureClient(
         const sp<ISurfaceTexture>& surfaceTexture):
-        mSurfaceTexture(surfaceTexture), mAllocator(0), mReqWidth(1),
-        mReqHeight(1), mReqFormat(DEFAULT_FORMAT), mReqUsage(0),
+        mSurfaceTexture(surfaceTexture), mAllocator(0), mReqWidth(0),
+        mReqHeight(0), mReqFormat(DEFAULT_FORMAT), mReqUsage(0),
         mTimestamp(NATIVE_WINDOW_TIMESTAMP_AUTO), mMutex() {
     // Initialize the ANativeWindow function pointers.
     ANativeWindow::setSwapInterval  = setSwapInterval;
@@ -100,7 +100,8 @@
         return err;
     }
     sp<GraphicBuffer>& gbuf(mSlots[buf]);
-    if (gbuf == 0 || gbuf->getWidth() != mReqWidth ||
+    if (err == ISurfaceTexture::BUFFER_NEEDS_REALLOCATION ||
+        gbuf == 0 || gbuf->getWidth() != mReqWidth ||
         gbuf->getHeight() != mReqHeight ||
         uint32_t(gbuf->getPixelFormat()) != mReqFormat ||
         (gbuf->getUsage() & mReqUsage) != mReqUsage) {
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 94b05bc..348171d 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -100,4 +100,151 @@
     eglTerminate(dpy);
 }
 
+TEST_F(SurfaceTextureClientTest, BufferGeometryInvalidSizesFail) {
+    sp<ANativeWindow> anw(mSTC);
+
+    EXPECT_GT(OK, native_window_set_buffers_geometry(anw.get(), -1,  0,  0));
+    EXPECT_GT(OK, native_window_set_buffers_geometry(anw.get(),  0, -1,  0));
+    EXPECT_GT(OK, native_window_set_buffers_geometry(anw.get(),  0,  0, -1));
+    EXPECT_GT(OK, native_window_set_buffers_geometry(anw.get(), -1, -1,  0));
+    EXPECT_GT(OK, native_window_set_buffers_geometry(anw.get(),  0,  8,  0));
+    EXPECT_GT(OK, native_window_set_buffers_geometry(anw.get(),  8,  0,  0));
+}
+
+TEST_F(SurfaceTextureClientTest, DefaultGeometryValues) {
+    sp<ANativeWindow> anw(mSTC);
+    android_native_buffer_t* buf;
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+    EXPECT_EQ(1, buf->width);
+    EXPECT_EQ(1, buf->height);
+    EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+}
+
+TEST_F(SurfaceTextureClientTest, BufferGeometryCanBeSet) {
+    sp<ANativeWindow> anw(mSTC);
+    android_native_buffer_t* buf;
+    EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 16, 8, PIXEL_FORMAT_RGB_565));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+    EXPECT_EQ(16, buf->width);
+    EXPECT_EQ(8, buf->height);
+    EXPECT_EQ(PIXEL_FORMAT_RGB_565, buf->format);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+}
+
+TEST_F(SurfaceTextureClientTest, BufferGeometryDefaultSizeSetFormat) {
+    sp<ANativeWindow> anw(mSTC);
+    android_native_buffer_t* buf;
+    EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 0, 0, PIXEL_FORMAT_RGB_565));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+    EXPECT_EQ(1, buf->width);
+    EXPECT_EQ(1, buf->height);
+    EXPECT_EQ(PIXEL_FORMAT_RGB_565, buf->format);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+}
+
+TEST_F(SurfaceTextureClientTest, BufferGeometrySetSizeDefaultFormat) {
+    sp<ANativeWindow> anw(mSTC);
+    android_native_buffer_t* buf;
+    EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 16, 8, 0));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+    EXPECT_EQ(16, buf->width);
+    EXPECT_EQ(8, buf->height);
+    EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+}
+
+TEST_F(SurfaceTextureClientTest, BufferGeometrySizeCanBeUnset) {
+    sp<ANativeWindow> anw(mSTC);
+    android_native_buffer_t* buf;
+    EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 16, 8, 0));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+    EXPECT_EQ(16, buf->width);
+    EXPECT_EQ(8, buf->height);
+    EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+    EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 0, 0, 0));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+    EXPECT_EQ(1, buf->width);
+    EXPECT_EQ(1, buf->height);
+    EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+}
+
+TEST_F(SurfaceTextureClientTest, BufferGeometrySizeCanBeChangedWithoutFormat) {
+    sp<ANativeWindow> anw(mSTC);
+    android_native_buffer_t* buf;
+    EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 0, 0, PIXEL_FORMAT_RGB_565));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+    EXPECT_EQ(1, buf->width);
+    EXPECT_EQ(1, buf->height);
+    EXPECT_EQ(PIXEL_FORMAT_RGB_565, buf->format);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+    EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 16, 8, 0));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+    EXPECT_EQ(16, buf->width);
+    EXPECT_EQ(8, buf->height);
+    EXPECT_EQ(PIXEL_FORMAT_RGB_565, buf->format);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSize) {
+    sp<ANativeWindow> anw(mSTC);
+    sp<SurfaceTexture> st(mST);
+    android_native_buffer_t* buf;
+    EXPECT_EQ(OK, st->setDefaultBufferSize(16, 8));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+    EXPECT_EQ(16, buf->width);
+    EXPECT_EQ(8, buf->height);
+    EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSizeAfterDequeue) {
+    sp<ANativeWindow> anw(mSTC);
+    sp<SurfaceTexture> st(mST);
+    android_native_buffer_t* buf[2];
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+    EXPECT_NE(buf[0], buf[1]);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[0]));
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[1]));
+    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]));
+    EXPECT_NE(buf[0], buf[1]);
+    EXPECT_EQ(16, buf[0]->width);
+    EXPECT_EQ(16, buf[1]->width);
+    EXPECT_EQ(8, buf[0]->height);
+    EXPECT_EQ(8, buf[1]->height);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[0]));
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[1]));
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSizeVsGeometry) {
+    sp<ANativeWindow> anw(mSTC);
+    sp<SurfaceTexture> st(mST);
+    android_native_buffer_t* buf[2];
+    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]));
+    EXPECT_NE(buf[0], buf[1]);
+    EXPECT_EQ(16, buf[0]->width);
+    EXPECT_EQ(16, buf[1]->width);
+    EXPECT_EQ(8, buf[0]->height);
+    EXPECT_EQ(8, buf[1]->height);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[0]));
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[1]));
+    EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 12, 24, 0));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+    EXPECT_NE(buf[0], buf[1]);
+    EXPECT_EQ(12, buf[0]->width);
+    EXPECT_EQ(12, buf[1]->width);
+    EXPECT_EQ(24, buf[0]->height);
+    EXPECT_EQ(24, buf[1]->height);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[0]));
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[1]));
+}
+
 }