diff --git a/include/gui/ISurfaceTexture.h b/include/gui/ISurfaceTexture.h index 6ed3c6f47..d2d3bb8f8 100644 --- a/include/gui/ISurfaceTexture.h +++ b/include/gui/ISurfaceTexture.h @@ -36,6 +36,8 @@ class ISurfaceTexture : public IInterface public: DECLARE_META_INTERFACE(SurfaceTexture); + enum { BUFFER_NEEDS_REALLOCATION = 1 }; + // requestBuffer requests a new buffer for the given index. The server (i.e. // the ISurfaceTexture implementation) assigns the newly created buffer to // the given slot index, and the client is expected to mirror the @@ -56,6 +58,8 @@ public: // should call requestBuffer to assign a new buffer to that slot. The client // is expected to either call cancelBuffer on the dequeued slot or to fill // in the contents of its associated buffer contents and call queueBuffer. + // If dequeueBuffer return BUFFER_NEEDS_REALLOCATION, the client is + // expected to call requestBuffer immediately. virtual status_t dequeueBuffer(int *slot) = 0; // queueBuffer indicates that the client has finished filling in the diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h index afa64d30d..585d288ed 100644 --- a/include/gui/SurfaceTexture.h +++ b/include/gui/SurfaceTexture.h @@ -121,6 +121,12 @@ public: // buffers before the client is done with them. sp getAllocator(); + // setDefaultBufferSize is used to set the size of buffers returned by + // requestBuffers when a with and height of zero is requested. + // A call to setDefaultBufferSize() may trigger requestBuffers() to + // be called from the client. + status_t setDefaultBufferSize(uint32_t w, uint32_t h); + private: // freeAllBuffers frees the resources (both GraphicBuffer and EGLImage) for @@ -158,6 +164,23 @@ private: // for a slot when requestBuffer is called with that slot's index. BufferSlot mSlots[NUM_BUFFER_SLOTS]; + // mDefaultWidth holds the default width of allocated buffers. It is used + // in requestBuffers() if a width and height of zero is specified. + uint32_t mDefaultWidth; + + // mDefaultHeight holds the default height of allocated buffers. It is used + // in requestBuffers() if a width and height of zero is specified. + uint32_t mDefaultHeight; + + // mPixelFormat holds the pixel format of allocated buffers. It is used + // in requestBuffers() if a format of zero is specified. + uint32_t mPixelFormat; + + // mUseDefaultSize indicates whether or not the default size should be used + // that is, if the last requestBuffer has been called with both width + // and height null. + bool mUseDefaultSize; + // mBufferCount is the number of buffer slots that the client and server // must maintain. It defaults to MIN_BUFFER_SLOTS and can be changed by // calling setBufferCount. diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index cdaca47b6..f4e2a6761 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -76,6 +76,10 @@ static float mtxRot270[16] = { 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 @@ status_t SurfaceTexture::setBufferCount(int bufferCount) { 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 SurfaceTexture::requestBuffer(int buf, uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { LOGV("SurfaceTexture::requestBuffer"); @@ -124,12 +138,34 @@ sp SurfaceTexture::requestBuffer(int buf, 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( 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 @@ status_t SurfaceTexture::dequeueBuffer(int *buf) { if (found == INVALID_BUFFER_SLOT) { return -EBUSY; } + *buf = found; + + const sp& 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 @@ void SurfaceTexture::getTransformMatrix(float mtx[16]) { } 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 a4812d073..29fc4d3ed 100644 --- a/libs/gui/SurfaceTextureClient.cpp +++ b/libs/gui/SurfaceTextureClient.cpp @@ -25,8 +25,8 @@ namespace android { SurfaceTextureClient::SurfaceTextureClient( const sp& 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 @@ int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer) { return err; } sp& 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 94b05bc2d..348171dcf 100644 --- a/libs/gui/tests/SurfaceTextureClient_test.cpp +++ b/libs/gui/tests/SurfaceTextureClient_test.cpp @@ -100,4 +100,151 @@ TEST_F(SurfaceTextureClientTest, EglCreateWindowSurfaceFails) { eglTerminate(dpy); } +TEST_F(SurfaceTextureClientTest, BufferGeometryInvalidSizesFail) { + sp 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 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 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 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 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 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 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 anw(mSTC); + sp 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 anw(mSTC); + sp 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 anw(mSTC); + sp 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])); +} + }