From 2a9fc493dfdba67108e4335bb1fe931bc1e2a025 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 1 Mar 2013 13:42:57 -0800 Subject: [PATCH] rework screenshot API and implementation - SurfaceFlinger now supports to take a screenshot directly into an IGraphicBufferProducer - reimplement the IMemoryHeap screenshot on top of the above - reimplement LayerScreenshot such that its BufferQueue is directly used as the destination of the screenshot. LayerScreenshot is now a thin wrapper around Layer Bug: 6940974 Change-Id: I69a2096b44b91acbb99eba16f83a9c78d94e0d10 --- include/gui/ISurfaceComposer.h | 9 +- include/gui/SurfaceComposerClient.h | 9 + libs/gui/ISurfaceComposer.cpp | 34 +- libs/gui/SurfaceComposerClient.cpp | 11 + services/surfaceflinger/Layer.h | 4 + services/surfaceflinger/LayerScreenshot.cpp | 140 +------- services/surfaceflinger/LayerScreenshot.h | 31 +- services/surfaceflinger/SurfaceFlinger.cpp | 365 +++++++++++--------- services/surfaceflinger/SurfaceFlinger.h | 28 +- 9 files changed, 285 insertions(+), 346 deletions(-) diff --git a/include/gui/ISurfaceComposer.h b/include/gui/ISurfaceComposer.h index 48629498b..08eddcb67 100644 --- a/include/gui/ISurfaceComposer.h +++ b/include/gui/ISurfaceComposer.h @@ -103,7 +103,6 @@ public: uint32_t reqWidth, uint32_t reqHeight, uint32_t minLayerZ, uint32_t maxLayerZ) = 0; - /* triggers screen off and waits for it to complete */ virtual void blank(const sp& display) = 0; @@ -113,6 +112,11 @@ public: /* returns information about a display * intended to be used to get information about built-in displays */ virtual status_t getDisplayInfo(const sp& display, DisplayInfo* info) = 0; + + virtual status_t captureScreen(const sp& display, + const sp& producer, + uint32_t reqWidth, uint32_t reqHeight, + uint32_t minLayerZ, uint32_t maxLayerZ) = 0; }; // ---------------------------------------------------------------------------- @@ -130,11 +134,12 @@ public: GET_BUILT_IN_DISPLAY, SET_TRANSACTION_STATE, AUTHENTICATE_SURFACE, - CAPTURE_SCREEN, + CAPTURE_SCREEN_DEPRECATED, BLANK, UNBLANK, GET_DISPLAY_INFO, CONNECT_DISPLAY, + CAPTURE_SCREEN, }; virtual status_t onTransact(uint32_t code, const Parcel& data, diff --git a/include/gui/SurfaceComposerClient.h b/include/gui/SurfaceComposerClient.h index 9e4d6fc30..38c931d23 100644 --- a/include/gui/SurfaceComposerClient.h +++ b/include/gui/SurfaceComposerClient.h @@ -156,10 +156,19 @@ private: class ScreenshotClient { +public: + static status_t capture( + const sp& display, + const sp& producer, + uint32_t reqWidth, uint32_t reqHeight, + uint32_t minLayerZ, uint32_t maxLayerZ); + +private: sp mHeap; uint32_t mWidth; uint32_t mHeight; PixelFormat mFormat; + public: ScreenshotClient(); diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 72b627746..0a79ff763 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -115,7 +115,7 @@ public: data.writeInt32(reqHeight); data.writeInt32(minLayerZ); data.writeInt32(maxLayerZ); - remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply); + remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN_DEPRECATED, data, &reply); *heap = interface_cast(reply.readStrongBinder()); *width = reply.readInt32(); *height = reply.readInt32(); @@ -123,6 +123,23 @@ public: return reply.readInt32(); } + virtual status_t captureScreen(const sp& display, + const sp& producer, + uint32_t reqWidth, uint32_t reqHeight, + uint32_t minLayerZ, uint32_t maxLayerZ) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeStrongBinder(display); + data.writeStrongBinder(producer->asBinder()); + data.writeInt32(reqWidth); + data.writeInt32(reqHeight); + data.writeInt32(minLayerZ); + data.writeInt32(maxLayerZ); + remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply); + return reply.readInt32(); + } + virtual bool authenticateSurfaceTexture( const sp& bufferProducer) const { @@ -268,7 +285,7 @@ status_t BnSurfaceComposer::onTransact( CHECK_INTERFACE(ISurfaceComposer, data, reply); bootFinished(); } break; - case CAPTURE_SCREEN: { + case CAPTURE_SCREEN_DEPRECATED: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp display = data.readStrongBinder(); uint32_t reqWidth = data.readInt32(); @@ -286,6 +303,19 @@ status_t BnSurfaceComposer::onTransact( reply->writeInt32(f); reply->writeInt32(res); } break; + case CAPTURE_SCREEN: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp display = data.readStrongBinder(); + sp producer = + interface_cast(data.readStrongBinder()); + uint32_t reqWidth = data.readInt32(); + uint32_t reqHeight = data.readInt32(); + uint32_t minLayerZ = data.readInt32(); + uint32_t maxLayerZ = data.readInt32(); + status_t res = captureScreen(display, producer, + reqWidth, reqHeight, minLayerZ, maxLayerZ); + reply->writeInt32(res); + } break; case AUTHENTICATE_SURFACE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp bufferProducer = diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index e8e208f12..edfa78a35 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -606,6 +606,17 @@ void SurfaceComposerClient::unblankDisplay(const sp& token) { // ---------------------------------------------------------------------------- +status_t ScreenshotClient::capture( + const sp& display, + const sp& producer, + uint32_t reqWidth, uint32_t reqHeight, + uint32_t minLayerZ, uint32_t maxLayerZ) { + sp s(ComposerService::getComposerService()); + if (s == NULL) return NO_INIT; + return s->captureScreen(display, producer, + reqWidth, reqHeight, minLayerZ, maxLayerZ); +} + ScreenshotClient::ScreenshotClient() : mWidth(0), mHeight(0), mFormat(PIXEL_FORMAT_NONE) { } diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index a82767b75..5fd7e59f9 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -109,6 +109,10 @@ protected: virtual void dumpStats(String8& result, char* buffer, size_t SIZE) const; virtual void clearStats(); + sp getConsumer() const { + return mSurfaceFlingerConsumer; + } + private: // Creates an instance of ISurface for this Layer. virtual sp createSurface(); diff --git a/services/surfaceflinger/LayerScreenshot.cpp b/services/surfaceflinger/LayerScreenshot.cpp index f8009b330..3470d6708 100644 --- a/services/surfaceflinger/LayerScreenshot.cpp +++ b/services/surfaceflinger/LayerScreenshot.cpp @@ -18,152 +18,28 @@ #include #include -#include -#include - -#include -#include - -#include - #include "LayerScreenshot.h" #include "SurfaceFlinger.h" #include "DisplayDevice.h" - namespace android { // --------------------------------------------------------------------------- LayerScreenshot::LayerScreenshot(SurfaceFlinger* flinger, const sp& client) - : LayerBaseClient(flinger, client), - mTextureName(0), mFlinger(flinger), mIsSecure(false) + : Layer(flinger, client) { } -LayerScreenshot::~LayerScreenshot() +void LayerScreenshot::onFirstRef() { - if (mTextureName) { - mFlinger->deleteTextureAsync(mTextureName); - } -} + Layer::onFirstRef(); -status_t LayerScreenshot::captureLocked(int32_t layerStack) { - GLfloat u, v; - status_t result = mFlinger->renderScreenToTextureLocked(layerStack, - &mTextureName, &u, &v); - if (result != NO_ERROR) { - return result; - } - initTexture(u, v); - - // Currently screenshot always comes from the default display - mIsSecure = mFlinger->getDefaultDisplayDevice()->getSecureLayerVisible(); - - return NO_ERROR; -} - -status_t LayerScreenshot::capture() { - GLfloat u, v; - status_t result = mFlinger->renderScreenToTexture(0, &mTextureName, &u, &v); - if (result != NO_ERROR) { - return result; - } - initTexture(u, v); - - // Currently screenshot always comes from the default display - mIsSecure = mFlinger->getDefaultDisplayDevice()->getSecureLayerVisible(); - - return NO_ERROR; -} - -void LayerScreenshot::initTexture(GLfloat u, GLfloat v) { - glBindTexture(GL_TEXTURE_2D, mTextureName); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - mTexCoords[0] = 0; mTexCoords[1] = v; - mTexCoords[2] = 0; mTexCoords[3] = 0; - mTexCoords[4] = u; mTexCoords[5] = 0; - mTexCoords[6] = u; mTexCoords[7] = v; -} - -void LayerScreenshot::initStates(uint32_t w, uint32_t h, uint32_t flags) { - LayerBaseClient::initStates(w, h, flags); - if (!(flags & ISurfaceComposerClient::eHidden)) { - capture(); - } - if (flags & ISurfaceComposerClient::eSecure) { - ALOGW("ignoring surface flag eSecure - LayerScreenshot is considered " - "secure iff it captures the contents of a secure surface."); - } -} - -uint32_t LayerScreenshot::doTransaction(uint32_t flags) -{ - const LayerBase::State& draw(drawingState()); - const LayerBase::State& curr(currentState()); - - if (draw.flags & layer_state_t::eLayerHidden) { - if (!(curr.flags & layer_state_t::eLayerHidden)) { - // we're going from hidden to visible - status_t err = captureLocked(curr.layerStack); - if (err != NO_ERROR) { - ALOGW("createScreenshotSurface failed (%s)", strerror(-err)); - } - } - } else if (curr.flags & layer_state_t::eLayerHidden) { - // we're going from visible to hidden - if (mTextureName) { - glDeleteTextures(1, &mTextureName); - mTextureName = 0; - } - } - return LayerBaseClient::doTransaction(flags); -} - -void LayerScreenshot::onDraw(const sp& hw, const Region& clip) const -{ - const State& s(drawingState()); - if (s.alpha>0) { - const GLfloat alpha = s.alpha/255.0f; - const uint32_t fbHeight = hw->getHeight(); - - if (s.alpha == 0xFF) { - glDisable(GL_BLEND); - glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - } else { - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - } - - GLuint texName = mTextureName; - if (isSecure() && !hw->isSecure()) { - texName = mFlinger->getProtectedTexName(); - } - - LayerMesh mesh; - computeGeometry(hw, &mesh); - - glColor4f(alpha, alpha, alpha, alpha); - - glDisable(GL_TEXTURE_EXTERNAL_OES); - glEnable(GL_TEXTURE_2D); - - glBindTexture(GL_TEXTURE_2D, texName); - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glTexCoordPointer(2, GL_FLOAT, 0, mTexCoords); - glVertexPointer(2, GL_FLOAT, 0, mesh.getVertices()); - glDrawArrays(GL_TRIANGLE_FAN, 0, mesh.getVertexCount()); - - glDisable(GL_BLEND); - glDisable(GL_TEXTURE_2D); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - } + // FIXME: we currently hardcode the default display + // it's unclear what should we do instead. + sp hw(mFlinger->getDefaultDisplayDevice()); + mFlinger->captureScreenImplLocked(hw, getConsumer()->getBufferQueue(), + 0, 0, 0, 0x7FFFFFFF); } // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/LayerScreenshot.h b/services/surfaceflinger/LayerScreenshot.h index 38cbd88fa..a2ae03f81 100644 --- a/services/surfaceflinger/LayerScreenshot.h +++ b/services/surfaceflinger/LayerScreenshot.h @@ -20,39 +20,18 @@ #include #include -#include -#include - -#include "LayerBase.h" +#include "Layer.h" // --------------------------------------------------------------------------- namespace android { -class LayerScreenshot : public LayerBaseClient +class LayerScreenshot : public Layer { - GLuint mTextureName; - GLfloat mTexCoords[8]; - sp mFlinger; - bool mIsSecure; public: - LayerScreenshot(SurfaceFlinger* flinger, const sp& client); - virtual ~LayerScreenshot(); - - status_t capture(); - - virtual void initStates(uint32_t w, uint32_t h, uint32_t flags); - virtual uint32_t doTransaction(uint32_t flags); - virtual void onDraw(const sp& hw, const Region& clip) const; - virtual bool isOpaque() const { return false; } - virtual bool isSecure() const { return mIsSecure; } - virtual bool isProtectedByApp() const { return false; } - virtual bool isProtectedByDRM() const { return false; } - virtual const char* getTypeId() const { return "LayerScreenshot"; } - -private: - status_t captureLocked(int32_t layerStack); - void initTexture(GLfloat u, GLfloat v); + LayerScreenshot(SurfaceFlinger* flinger, const sp& client); +protected: + virtual void onFirstRef(); }; // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index ee3e93b1a..0971faac3 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -2020,6 +2020,7 @@ sp SurfaceFlinger::createScreenshotLayer( uint32_t w, uint32_t h, uint32_t flags) { sp layer = new LayerScreenshot(this, client); + layer->setBuffers(w, h, PIXEL_FORMAT_RGBA_8888, flags); return layer; } @@ -2603,101 +2604,69 @@ void SurfaceFlinger::repaintEverything() { } // --------------------------------------------------------------------------- - -status_t SurfaceFlinger::renderScreenToTexture(uint32_t layerStack, - GLuint* textureName, GLfloat* uOut, GLfloat* vOut) -{ - Mutex::Autolock _l(mStateLock); - return renderScreenToTextureLocked(layerStack, textureName, uOut, vOut); -} - -status_t SurfaceFlinger::renderScreenToTextureLocked(uint32_t layerStack, - GLuint* textureName, GLfloat* uOut, GLfloat* vOut) -{ - ATRACE_CALL(); - - if (!GLExtensions::getInstance().haveFramebufferObject()) - return INVALID_OPERATION; - - // get screen geometry - // FIXME: figure out what it means to have a screenshot texture w/ multi-display - sp hw(getDefaultDisplayDevice()); - const uint32_t hw_w = hw->getWidth(); - const uint32_t hw_h = hw->getHeight(); - GLfloat u = 1; - GLfloat v = 1; - - // make sure to clear all GL error flags - while ( glGetError() != GL_NO_ERROR ) ; - - // create a FBO - GLuint name, 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); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, - hw_w, hw_h, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); - if (glGetError() != GL_NO_ERROR) { - while ( glGetError() != GL_NO_ERROR ) ; - GLint tw = (2 << (31 - clz(hw_w))); - GLint th = (2 << (31 - clz(hw_h))); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, - tw, th, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); - u = GLfloat(hw_w) / tw; - v = GLfloat(hw_h) / th; - } - glGenFramebuffersOES(1, &name); - glBindFramebufferOES(GL_FRAMEBUFFER_OES, name); - glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, - GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, tname, 0); - - DisplayDevice::setViewportAndProjection(hw); - - // redraw the screen entirely... - glDisable(GL_TEXTURE_EXTERNAL_OES); - glDisable(GL_TEXTURE_2D); - glClearColor(0,0,0,1); - glClear(GL_COLOR_BUFFER_BIT); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - const Vector< sp >& layers(hw->getVisibleLayersSortedByZ()); - const size_t count = layers.size(); - for (size_t i=0 ; i& layer(layers[i]); - layer->draw(hw); - } - - hw->compositionComplete(); - - // back to main framebuffer - glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0); - glDeleteFramebuffersOES(1, &name); - - *textureName = tname; - *uOut = u; - *vOut = v; - return NO_ERROR; -} - +// Capture screen into an IGraphiBufferProducer // --------------------------------------------------------------------------- -status_t SurfaceFlinger::captureScreenImplLocked(const sp& display, - sp* heap, - uint32_t* w, uint32_t* h, PixelFormat* f, - uint32_t sw, uint32_t sh, +status_t SurfaceFlinger::captureScreen(const sp& display, + const sp& producer, + uint32_t reqWidth, uint32_t reqHeight, + uint32_t minLayerZ, uint32_t maxLayerZ) { + + if (CC_UNLIKELY(display == 0)) + return BAD_VALUE; + + if (CC_UNLIKELY(producer == 0)) + return BAD_VALUE; + + class MessageCaptureScreen : public MessageBase { + SurfaceFlinger* flinger; + sp display; + sp producer; + uint32_t reqWidth, reqHeight; + uint32_t minLayerZ,maxLayerZ; + status_t result; + public: + MessageCaptureScreen(SurfaceFlinger* flinger, + const sp& display, + const sp& producer, + uint32_t reqWidth, uint32_t reqHeight, + uint32_t minLayerZ, uint32_t maxLayerZ) + : flinger(flinger), display(display), producer(producer), + reqWidth(reqWidth), reqHeight(reqHeight), + minLayerZ(minLayerZ), maxLayerZ(maxLayerZ), + result(PERMISSION_DENIED) + { + } + status_t getResult() const { + return result; + } + virtual bool handler() { + Mutex::Autolock _l(flinger->mStateLock); + sp hw(flinger->getDisplayDevice(display)); + result = flinger->captureScreenImplLocked(hw, producer, + reqWidth, reqHeight, minLayerZ, maxLayerZ); + return true; + } + }; + + sp msg = new MessageCaptureScreen(this, + display, producer, reqWidth, reqHeight, minLayerZ, maxLayerZ); + status_t res = postMessageSync(msg); + if (res == NO_ERROR) { + res = static_cast( msg.get() )->getResult(); + } + return res; +} + +status_t SurfaceFlinger::captureScreenImplLocked( + const sp& hw, + const sp& producer, + uint32_t reqWidth, uint32_t reqHeight, uint32_t minLayerZ, uint32_t maxLayerZ) { ATRACE_CALL(); - status_t result = PERMISSION_DENIED; - - if (!GLExtensions::getInstance().haveFramebufferObject()) { - return INVALID_OPERATION; - } - // get screen geometry - sp hw(getDisplayDevice(display)); const uint32_t hw_w = hw->getWidth(); const uint32_t hw_h = hw->getHeight(); @@ -2707,68 +2676,128 @@ status_t SurfaceFlinger::captureScreenImplLocked(const sp& display, return PERMISSION_DENIED; } - if ((sw > hw_w) || (sh > hw_h)) { - ALOGE("size mismatch (%d, %d) > (%d, %d)", sw, sh, hw_w, hw_h); + if ((reqWidth > hw_w) || (reqHeight > hw_h)) { + ALOGE("size mismatch (%d, %d) > (%d, %d)", + reqWidth, reqHeight, hw_w, hw_h); return BAD_VALUE; } - sw = (!sw) ? hw_w : sw; - sh = (!sh) ? hw_h : sh; - const size_t size = sw * sh * 4; - const bool filtering = sw != hw_w || sh != hw_h; + reqWidth = (!reqWidth) ? hw_w : reqWidth; + reqHeight = (!reqHeight) ? hw_h : reqHeight; + const bool filtering = reqWidth != hw_w || reqWidth != hw_h; -// ALOGD("screenshot: sw=%d, sh=%d, minZ=%d, maxZ=%d", -// sw, sh, minLayerZ, maxLayerZ); + // Create a surface to render into + sp surface = new Surface(producer); + ANativeWindow* const window = surface.get(); + + // set the buffer size to what the user requested + native_window_set_buffers_user_dimensions(window, reqWidth, reqHeight); + + // and create the corresponding EGLSurface + EGLSurface eglSurface = eglCreateWindowSurface( + mEGLDisplay, mEGLConfig, window, NULL); + if (eglSurface == EGL_NO_SURFACE) { + ALOGE("captureScreenImplLocked: eglCreateWindowSurface() failed 0x%4x", + eglGetError()); + return BAD_VALUE; + } + + if (!eglMakeCurrent(mEGLDisplay, eglSurface, eglSurface, mEGLContext)) { + ALOGE("captureScreenImplLocked: eglMakeCurrent() failed 0x%4x", + eglGetError()); + eglDestroySurface(mEGLDisplay, eglSurface); + return BAD_VALUE; + } // make sure to clear all GL error flags while ( glGetError() != GL_NO_ERROR ) ; - // create a FBO - GLuint name, tname; - glGenRenderbuffersOES(1, &tname); - glBindRenderbufferOES(GL_RENDERBUFFER_OES, tname); - glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_RGBA8_OES, sw, sh); + // 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(); - glGenFramebuffersOES(1, &name); - glBindFramebufferOES(GL_FRAMEBUFFER_OES, name); - glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, - GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname); + // redraw the screen entirely... + glDisable(GL_TEXTURE_EXTERNAL_OES); + glDisable(GL_TEXTURE_2D); + glClearColor(0,0,0,1); + glClear(GL_COLOR_BUFFER_BIT); - GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES); - - if (status == GL_FRAMEBUFFER_COMPLETE_OES) { - - // invert everything, b/c glReadPixel() below will invert the FB - GLint viewport[4]; - glGetIntegerv(GL_VIEWPORT, viewport); - glViewport(0, 0, sw, sh); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrthof(0, hw_w, hw_h, 0, 0, 1); - glMatrixMode(GL_MODELVIEW); - - // redraw the screen entirely... - glClearColor(0,0,0,1); - glClear(GL_COLOR_BUFFER_BIT); - - const Vector< sp >& layers(hw->getVisibleLayersSortedByZ()); - const size_t count = layers.size(); - for (size_t i=0 ; i& layer(layers[i]); - const uint32_t z = layer->drawingState().z; - if (z >= minLayerZ && z <= maxLayerZ) { - if (filtering) layer->setFiltering(true); - layer->draw(hw); - if (filtering) layer->setFiltering(false); - } + const Vector< sp >& layers(hw->getVisibleLayersSortedByZ()); + const size_t count = layers.size(); + for (size_t i=0 ; i& layer(layers[i]); + const uint32_t z = layer->drawingState().z; + if (z >= minLayerZ && z <= maxLayerZ) { + if (filtering) layer->setFiltering(true); + layer->draw(hw); + if (filtering) layer->setFiltering(false); } + } + + // and finishing things up... + if (eglSwapBuffers(mEGLDisplay, eglSurface) != EGL_TRUE) { + ALOGE("captureScreenImplLocked: eglSwapBuffers() failed 0x%4x", + eglGetError()); + eglDestroySurface(mEGLDisplay, eglSurface); + return BAD_VALUE; + } + + eglDestroySurface(mEGLDisplay, eglSurface); + return NO_ERROR; +} + +// --------------------------------------------------------------------------- +// Capture screen into an IMemoryHeap (legacy) +// --------------------------------------------------------------------------- + +status_t SurfaceFlinger::captureScreenImplLocked( + const sp& hw, + sp* heap, + uint32_t* w, uint32_t* h, PixelFormat* f, + uint32_t sw, uint32_t sh, + uint32_t minLayerZ, uint32_t maxLayerZ) +{ + ATRACE_CALL(); + + if (!GLExtensions::getInstance().haveFramebufferObject()) { + return INVALID_OPERATION; + } + + // create the texture that will receive the screenshot, later we'll + // attach a FBO to it so we can call glReadPixels(). + 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); + + // the GLConsumer will provide the BufferQueue + sp consumer = new GLConsumer(tname, true, GL_TEXTURE_2D); + consumer->getBufferQueue()->setDefaultBufferFormat(HAL_PIXEL_FORMAT_RGBA_8888); + + // call the new screenshot taking code, passing a BufferQueue to it + status_t result = captureScreenImplLocked(hw, + consumer->getBufferQueue(), sw, sh, minLayerZ, maxLayerZ); + + 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); + + sp buf(consumer->getCurrentBuffer()); + sw = buf->getWidth(); + sh = buf->getHeight(); + size_t size = buf->getStride() * sh * 4; - // check for errors and return screen capture - if (glGetError() != GL_NO_ERROR) { - // error while rendering - result = INVALID_OPERATION; - } else { // allocate shared memory large enough to hold the // screen capture sp base( @@ -2788,59 +2817,48 @@ status_t SurfaceFlinger::captureScreenImplLocked(const sp& display, } else { result = NO_MEMORY; } + + // back to main framebuffer + glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0); + glDeleteFramebuffersOES(1, &name); } - glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - } else { - result = BAD_VALUE; } - // release FBO resources - glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0); - glDeleteRenderbuffersOES(1, &tname); - glDeleteFramebuffersOES(1, &name); - - hw->compositionComplete(); - -// ALOGD("screenshot: result = %s", result<0 ? strerror(result) : "OK"); + glDeleteTextures(1, &tname); return result; } - status_t SurfaceFlinger::captureScreen(const sp& display, sp* heap, - uint32_t* width, uint32_t* height, PixelFormat* format, - uint32_t sw, uint32_t sh, + uint32_t* outWidth, uint32_t* outHeight, PixelFormat* outFormat, + uint32_t reqWidth, uint32_t reqHeight, uint32_t minLayerZ, uint32_t maxLayerZ) { if (CC_UNLIKELY(display == 0)) return BAD_VALUE; - if (!GLExtensions::getInstance().haveFramebufferObject()) - return INVALID_OPERATION; - class MessageCaptureScreen : public MessageBase { SurfaceFlinger* flinger; sp display; sp* heap; - uint32_t* w; - uint32_t* h; - PixelFormat* f; - uint32_t sw; - uint32_t sh; + uint32_t* outWidth; + uint32_t* outHeight; + PixelFormat* outFormat; + uint32_t reqWidth; + uint32_t reqHeight; uint32_t minLayerZ; uint32_t maxLayerZ; status_t result; public: - MessageCaptureScreen(SurfaceFlinger* flinger, const sp& display, - sp* heap, uint32_t* w, uint32_t* h, PixelFormat* f, - uint32_t sw, uint32_t sh, + MessageCaptureScreen(SurfaceFlinger* flinger, + const sp& display, sp* heap, + uint32_t* outWidth, uint32_t* outHeight, PixelFormat* outFormat, + uint32_t reqWidth, uint32_t reqHeight, uint32_t minLayerZ, uint32_t maxLayerZ) - : flinger(flinger), display(display), - heap(heap), w(w), h(h), f(f), sw(sw), sh(sh), + : flinger(flinger), display(display), heap(heap), + outWidth(outWidth), outHeight(outHeight), outFormat(outFormat), + reqWidth(reqWidth), reqHeight(reqHeight), minLayerZ(minLayerZ), maxLayerZ(maxLayerZ), result(PERMISSION_DENIED) { @@ -2850,14 +2868,17 @@ status_t SurfaceFlinger::captureScreen(const sp& display, } virtual bool handler() { Mutex::Autolock _l(flinger->mStateLock); - result = flinger->captureScreenImplLocked(display, - heap, w, h, f, sw, sh, minLayerZ, maxLayerZ); + sp hw(flinger->getDisplayDevice(display)); + result = flinger->captureScreenImplLocked(hw, heap, + outWidth, outHeight, outFormat, + reqWidth, reqHeight, minLayerZ, maxLayerZ); return true; } }; - sp msg = new MessageCaptureScreen(this, - display, heap, width, height, format, sw, sh, minLayerZ, maxLayerZ); + sp msg = new MessageCaptureScreen(this, display, heap, + outWidth, outHeight, outFormat, + reqWidth, reqHeight, minLayerZ, maxLayerZ); status_t res = postMessageSync(msg); if (res == NO_ERROR) { res = static_cast( msg.get() )->getResult(); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index e67f3f187..d1221dc8a 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -103,14 +103,6 @@ public: // force full composition on all displays void repaintEverything(); - // renders content on given display to a texture. thread-safe version. - status_t renderScreenToTexture(uint32_t layerStack, GLuint* textureName, - GLfloat* uOut, GLfloat* vOut); - - // renders content on given display to a texture, w/o acquiring main lock - status_t renderScreenToTextureLocked(uint32_t layerStack, GLuint* textureName, - GLfloat* uOut, GLfloat* vOut); - // returns the default Display sp getDefaultDisplayDevice() const { return getDisplayDevice(mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY]); @@ -202,6 +194,10 @@ private: uint32_t* width, uint32_t* height, PixelFormat* format, uint32_t reqWidth, uint32_t reqHeight, uint32_t minLayerZ, uint32_t maxLayerZ); + virtual status_t captureScreen(const sp& display, + const sp& producer, + uint32_t reqWidth, uint32_t reqHeight, + uint32_t minLayerZ, uint32_t maxLayerZ); // called when screen needs to turn off virtual void blank(const sp& display); // called when screen is turning back on @@ -306,10 +302,18 @@ private: void startBootAnim(); - status_t captureScreenImplLocked(const sp& display, sp* heap, - uint32_t* width, uint32_t* height, PixelFormat* format, - uint32_t reqWidth, uint32_t reqHeight, uint32_t minLayerZ, - uint32_t maxLayerZ); + status_t captureScreenImplLocked( + const sp& hw, + sp* heap, + uint32_t* width, uint32_t* height, PixelFormat* format, + uint32_t reqWidth, uint32_t reqHeight, uint32_t minLayerZ, + uint32_t maxLayerZ); + + status_t captureScreenImplLocked( + const sp& hw, + const sp& producer, + uint32_t reqWidth, uint32_t reqHeight, + uint32_t minLayerZ, uint32_t maxLayerZ); /* ------------------------------------------------------------------------ * EGL