summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMathias Agopian <mathias@google.com>2013-04-10 22:55:41 -0700
committerThe Android Automerger <android-build@android.com>2013-04-11 23:28:25 -0700
commitb90d8dc527f02687b36f0ab766855024b7bdb4a8 (patch)
tree8c4bb55925067d105376ae9708130870cfc715e7
parent6bd8dee87e96365e3ceb901a5ef935b32ca84cd9 (diff)
downloadframeworks_native-b90d8dc527f02687b36f0ab766855024b7bdb4a8.zip
frameworks_native-b90d8dc527f02687b36f0ab766855024b7bdb4a8.tar.gz
frameworks_native-b90d8dc527f02687b36f0ab766855024b7bdb4a8.tar.bz2
Improve screenshot performance on some devices (DO NOT MERGE)
this affects devices that need a glReadPixels(). We use a FBO instead of a GlConsumer as an intermediate render target, this saves 2 calls to eglMakeCurrent(). On Galaxy Nexus this allows us to go from ~135ms to ~35ms for recent's screenshots. Bug: 8582615 Change-Id: I6b25291ecc235f1927579bbb2db3c731e985c6e8
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp245
-rw-r--r--services/surfaceflinger/SurfaceFlinger.h6
2 files changed, 131 insertions, 120 deletions
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 32b97eb..4bc0abd 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2646,6 +2646,61 @@ status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display,
return res;
}
+
+void SurfaceFlinger::renderScreenImplLocked(
+ const sp<const DisplayDevice>& hw,
+ uint32_t reqWidth, uint32_t reqHeight,
+ uint32_t minLayerZ, uint32_t maxLayerZ,
+ bool yswap)
+{
+ ATRACE_CALL();
+
+ // get screen geometry
+ const uint32_t hw_w = hw->getWidth();
+ const uint32_t hw_h = hw->getHeight();
+
+ const bool filtering = reqWidth != hw_w || reqWidth != hw_h;
+
+ // make sure to clear all GL error flags
+ while ( glGetError() != GL_NO_ERROR ) ;
+
+ // set-up our viewport
+ glViewport(0, 0, reqWidth, reqHeight);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ if (yswap) glOrthof(0, hw_w, hw_h, 0, 0, 1);
+ else glOrthof(0, hw_w, 0, hw_h, 0, 1);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ // redraw the screen entirely...
+ glDisable(GL_SCISSOR_TEST);
+ glClearColor(0,0,0,1);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glDisable(GL_TEXTURE_EXTERNAL_OES);
+ glDisable(GL_TEXTURE_2D);
+
+ const LayerVector& layers( mDrawingState.layersSortedByZ );
+ const size_t count = layers.size();
+ for (size_t i=0 ; i<count ; ++i) {
+ const sp<Layer>& layer(layers[i]);
+ const Layer::State& state(layer->drawingState());
+ if (state.layerStack == hw->getLayerStack()) {
+ if (state.z >= minLayerZ && state.z <= maxLayerZ) {
+ if (layer->isVisible()) {
+ if (filtering) layer->setFiltering(true);
+ layer->draw(hw);
+ if (filtering) layer->setFiltering(false);
+ }
+ }
+ }
+ }
+
+ // compositionComplete is needed for older driver
+ hw->compositionComplete();
+}
+
+
status_t SurfaceFlinger::captureScreenImplLocked(
const sp<const DisplayDevice>& hw,
const sp<IGraphicBufferProducer>& producer,
@@ -2672,7 +2727,6 @@ status_t SurfaceFlinger::captureScreenImplLocked(
reqWidth = (!reqWidth) ? hw_w : reqWidth;
reqHeight = (!reqHeight) ? hw_h : reqHeight;
- const bool filtering = reqWidth != hw_w || reqWidth != hw_h;
// Create a surface to render into
sp<Surface> surface = new Surface(producer);
@@ -2697,41 +2751,7 @@ status_t SurfaceFlinger::captureScreenImplLocked(
return BAD_VALUE;
}
- // make sure to clear all GL error flags
- while ( glGetError() != GL_NO_ERROR ) ;
-
- // set-up our viewport
- glViewport(0, 0, reqWidth, reqHeight);
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glOrthof(0, hw_w, 0, hw_h, 0, 1);
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
-
- // redraw the screen entirely...
- glDisable(GL_TEXTURE_EXTERNAL_OES);
- glDisable(GL_TEXTURE_2D);
- glClearColor(0,0,0,1);
- glClear(GL_COLOR_BUFFER_BIT);
-
- const LayerVector& layers( mDrawingState.layersSortedByZ );
- const size_t count = layers.size();
- for (size_t i=0 ; i<count ; ++i) {
- const sp<Layer>& layer(layers[i]);
- const Layer::State& state(layer->drawingState());
- if (state.layerStack == hw->getLayerStack()) {
- if (state.z >= minLayerZ && state.z <= maxLayerZ) {
- if (layer->isVisible()) {
- if (filtering) layer->setFiltering(true);
- layer->draw(hw);
- if (filtering) layer->setFiltering(false);
- }
- }
- }
- }
-
- // compositionComplete is needed for older driver
- hw->compositionComplete();
+ renderScreenImplLocked(hw, reqWidth, reqHeight, minLayerZ, maxLayerZ, false);
// and finishing things up...
if (eglSwapBuffers(mEGLDisplay, eglSurface) != EGL_TRUE) {
@@ -2759,102 +2779,87 @@ status_t SurfaceFlinger::captureScreenImplCpuConsumerLocked(
return INVALID_OPERATION;
}
- // create the texture that will receive the screenshot, later we'll
- // attach a FBO to it so we can call glReadPixels().
+ // get screen geometry
+ const uint32_t hw_w = hw->getWidth();
+ const uint32_t hw_h = hw->getHeight();
+
+ // if we have secure windows on this display, never allow the screen capture
+ if (hw->getSecureLayerVisible()) {
+ ALOGW("FB is protected: PERMISSION_DENIED");
+ return PERMISSION_DENIED;
+ }
+
+ if ((reqWidth > hw_w) || (reqHeight > hw_h)) {
+ ALOGE("size mismatch (%d, %d) > (%d, %d)",
+ reqWidth, reqHeight, hw_w, hw_h);
+ return BAD_VALUE;
+ }
+
+ reqWidth = (!reqWidth) ? hw_w : reqWidth;
+ reqHeight = (!reqHeight) ? hw_h : reqHeight;
+
GLuint tname;
- glGenTextures(1, &tname);
- glBindTexture(GL_TEXTURE_2D, tname);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glGenRenderbuffersOES(1, &tname);
+ glBindRenderbufferOES(GL_RENDERBUFFER_OES, tname);
+ glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_RGBA8_OES, reqWidth, reqHeight);
- // the GLConsumer will provide the BufferQueue
- sp<GLConsumer> consumer = new GLConsumer(tname, true, GL_TEXTURE_2D);
- consumer->getBufferQueue()->setDefaultBufferFormat(HAL_PIXEL_FORMAT_RGBA_8888);
+ // create a FBO
+ GLuint name;
+ glGenFramebuffersOES(1, &name);
+ glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
+ glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
+ GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname);
- // call the new screenshot taking code, passing a BufferQueue to it
- status_t result = captureScreenImplLocked(hw,
- consumer->getBufferQueue(), reqWidth, reqHeight, minLayerZ, maxLayerZ);
+ GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
- if (result == NO_ERROR) {
- result = consumer->updateTexImage();
- if (result == NO_ERROR) {
- // create a FBO
- GLuint name;
- glGenFramebuffersOES(1, &name);
- glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
- glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES,
- GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, tname, 0);
-
- reqWidth = consumer->getCurrentBuffer()->getWidth();
- reqHeight = consumer->getCurrentBuffer()->getHeight();
-
- {
- // in this block we render the screenshot into the
- // CpuConsumer using glReadPixels from our GLConsumer,
- // Some older drivers don't support the GL->CPU path so
- // have to wrap it with a CPU->CPU path, which is what
- // glReadPixels essentially is
-
- sp<Surface> sur = new Surface(producer);
- ANativeWindow* window = sur.get();
+ status_t result = NO_ERROR;
+ if (status == GL_FRAMEBUFFER_COMPLETE_OES) {
+
+ renderScreenImplLocked(hw, reqWidth, reqHeight, minLayerZ, maxLayerZ, true);
+
+ // Below we render the screenshot into the
+ // CpuConsumer using glReadPixels from our FBO.
+ // Some older drivers don't support the GL->CPU path so we
+ // have to wrap it with a CPU->CPU path, which is what
+ // glReadPixels essentially is.
+
+ sp<Surface> sur = new Surface(producer);
+ ANativeWindow* window = sur.get();
+
+ if (native_window_api_connect(window, NATIVE_WINDOW_API_CPU) == NO_ERROR) {
+ int err = 0;
+ err = native_window_set_buffers_dimensions(window, reqWidth, reqHeight);
+ err |= native_window_set_buffers_format(window, HAL_PIXEL_FORMAT_RGBA_8888);
+ err |= native_window_set_usage(window,
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
+
+ if (err == NO_ERROR) {
ANativeWindowBuffer* buffer;
- void* vaddr;
-
- if (native_window_api_connect(window,
- NATIVE_WINDOW_API_CPU) == NO_ERROR) {
- int err = 0;
- err = native_window_set_buffers_dimensions(window,
- reqWidth, reqHeight);
- err |= native_window_set_buffers_format(window,
- HAL_PIXEL_FORMAT_RGBA_8888);
- err |= native_window_set_usage(window,
- GRALLOC_USAGE_SW_READ_OFTEN |
- GRALLOC_USAGE_SW_WRITE_OFTEN);
-
- if (err == NO_ERROR) {
- if (native_window_dequeue_buffer_and_wait(window,
- &buffer) == NO_ERROR) {
- sp<GraphicBuffer> buf =
- static_cast<GraphicBuffer*>(buffer);
- if (buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN,
- &vaddr) == NO_ERROR) {
- if (buffer->stride != int(reqWidth)) {
- // we're unlucky here, glReadPixels is
- // not able to deal with a stride not
- // equal to the width.
- uint32_t* tmp = new uint32_t[reqWidth*reqHeight];
- if (tmp != NULL) {
- glReadPixels(0, 0, reqWidth, reqHeight,
- GL_RGBA, GL_UNSIGNED_BYTE, tmp);
- for (size_t y=0 ; y<reqHeight ; y++) {
- memcpy((uint32_t*)vaddr + y*buffer->stride,
- tmp + y*reqWidth, reqWidth*4);
- }
- delete [] tmp;
- }
- } else {
- glReadPixels(0, 0, reqWidth, reqHeight,
- GL_RGBA, GL_UNSIGNED_BYTE, vaddr);
- }
- buf->unlock();
- }
- window->queueBuffer(window, buffer, -1);
- }
+ if (native_window_dequeue_buffer_and_wait(window, &buffer) == NO_ERROR) {
+ sp<GraphicBuffer> buf = static_cast<GraphicBuffer*>(buffer);
+ void* vaddr;
+ if (buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, &vaddr) == NO_ERROR) {
+ glReadPixels(0, 0, buffer->stride, reqHeight,
+ GL_RGBA, GL_UNSIGNED_BYTE, vaddr);
+ buf->unlock();
}
- native_window_api_disconnect(window, NATIVE_WINDOW_API_CPU);
+ window->queueBuffer(window, buffer, -1);
}
}
-
- // back to main framebuffer
- glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
- glDeleteFramebuffersOES(1, &name);
+ native_window_api_disconnect(window, NATIVE_WINDOW_API_CPU);
}
+
+ } else {
+ ALOGE("got GL_FRAMEBUFFER_COMPLETE_OES while taking screenshot");
+ result = INVALID_OPERATION;
}
- glDeleteTextures(1, &tname);
+ // back to main framebuffer
+ glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
+ glDeleteRenderbuffersOES(1, &tname);
+ glDeleteFramebuffersOES(1, &name);
- DisplayDevice::makeCurrent(mEGLDisplay,
- getDefaultDisplayDevice(), mEGLContext);
+ DisplayDevice::setViewportAndProjection(hw);
return result;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 57ee8b9..739099c 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -288,6 +288,12 @@ private:
void startBootAnim();
+ void renderScreenImplLocked(
+ const sp<const DisplayDevice>& hw,
+ uint32_t reqWidth, uint32_t reqHeight,
+ uint32_t minLayerZ, uint32_t maxLayerZ,
+ bool yswap);
+
status_t captureScreenImplLocked(
const sp<const DisplayDevice>& hw,
const sp<IGraphicBufferProducer>& producer,