diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 902570b93..ef0d52146 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -2571,6 +2571,86 @@ void SurfaceFlinger::repaintEverything() { // Capture screen into an IGraphiBufferProducer // --------------------------------------------------------------------------- +/* The code below is here to handle b/8734824 + * + * We create a IGraphicBufferProducer wrapper that forwards all calls + * to the calling binder thread, where they are executed. This allows + * the calling thread to be reused (on the other side) and not + * depend on having "enough" binder threads to handle the requests. + * + */ + +class GraphicProducerWrapper : public BBinder, public MessageHandler { + sp impl; + sp looper; + status_t result; + bool exitPending; + bool exitRequested; + mutable Barrier barrier; + volatile int32_t memoryBarrier; + uint32_t code; + Parcel const* data; + Parcel* reply; + + enum { + MSG_API_CALL, + MSG_EXIT + }; + + /* + * this is called by our "fake" BpGraphicBufferProducer. We package the + * data and reply Parcel and forward them to the calling thread. + */ + virtual status_t transact(uint32_t code, + const Parcel& data, Parcel* reply, uint32_t flags) { + this->code = code; + this->data = &data; + this->reply = reply; + android_atomic_acquire_store(0, &memoryBarrier); + if (exitPending) { + // if we've exited, we run the message synchronously right here + handleMessage(Message(MSG_API_CALL)); + } else { + barrier.close(); + looper->sendMessage(this, Message(MSG_API_CALL)); + barrier.wait(); + } + return NO_ERROR; + } + + /* + * here we run on the binder calling thread. All we've got to do is + * call the real BpGraphicBufferProducer. + */ + virtual void handleMessage(const Message& message) { + android_atomic_release_load(&memoryBarrier); + if (message.what == MSG_API_CALL) { + impl->asBinder()->transact(code, data[0], reply); + barrier.open(); + } else if (message.what == MSG_EXIT) { + exitRequested = true; + } + } + +public: + GraphicProducerWrapper(const sp& impl) : + impl(impl), looper(new Looper(true)), result(NO_ERROR), + exitPending(false), exitRequested(false) { + } + + status_t waitForResponse() { + do { + looper->pollOnce(-1); + } while (!exitRequested); + return result; + } + + void exit(status_t result) { + exitPending = true; + looper->sendMessage(this, Message(MSG_EXIT)); + } +}; + status_t SurfaceFlinger::captureScreen(const sp& display, const sp& producer, uint32_t reqWidth, uint32_t reqHeight, @@ -2583,24 +2663,25 @@ status_t SurfaceFlinger::captureScreen(const sp& display, 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; - bool isCpuConsumer; + bool useReadPixels; 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, bool isCpuConsumer) + uint32_t minLayerZ, uint32_t maxLayerZ, bool useReadPixels) : flinger(flinger), display(display), producer(producer), reqWidth(reqWidth), reqHeight(reqHeight), minLayerZ(minLayerZ), maxLayerZ(maxLayerZ), - isCpuConsumer(isCpuConsumer), + useReadPixels(useReadPixels), result(PERMISSION_DENIED) { } @@ -2610,26 +2691,6 @@ status_t SurfaceFlinger::captureScreen(const sp& display, virtual bool handler() { Mutex::Autolock _l(flinger->mStateLock); sp hw(flinger->getDisplayDevice(display)); - - bool useReadPixels = false; - if (isCpuConsumer) { - bool formatSupportedBytBitmap = - (flinger->mEGLNativeVisualId == HAL_PIXEL_FORMAT_RGBA_8888) || - (flinger->mEGLNativeVisualId == HAL_PIXEL_FORMAT_RGBX_8888); - if (formatSupportedBytBitmap == false) { - // the pixel format we have is not compatible with - // Bitmap.java, which is the likely client of this API, - // so we just revert to glReadPixels() in that case. - useReadPixels = true; - } - if (flinger->mGpuToCpuSupported == false) { - // When we know the GL->CPU path works, we can call - // captureScreenImplLocked() directly, instead of using the - // glReadPixels() workaround. - useReadPixels = true; - } - } - if (!useReadPixels) { result = flinger->captureScreenImplLocked(hw, producer, reqWidth, reqHeight, minLayerZ, maxLayerZ); @@ -2637,6 +2698,7 @@ status_t SurfaceFlinger::captureScreen(const sp& display, result = flinger->captureScreenImplCpuConsumerLocked(hw, producer, reqWidth, reqHeight, minLayerZ, maxLayerZ); } + static_cast(producer->asBinder().get())->exit(result); return true; } }; @@ -2648,12 +2710,40 @@ status_t SurfaceFlinger::captureScreen(const sp& display, // scheduled at this time, this will end-up being a no-op as well. mEventQueue.invalidateTransactionNow(); + bool useReadPixels = false; + if (isCpuConsumer) { + bool formatSupportedBytBitmap = + (mEGLNativeVisualId == HAL_PIXEL_FORMAT_RGBA_8888) || + (mEGLNativeVisualId == HAL_PIXEL_FORMAT_RGBX_8888); + if (formatSupportedBytBitmap == false) { + // the pixel format we have is not compatible with + // Bitmap.java, which is the likely client of this API, + // so we just revert to glReadPixels() in that case. + useReadPixels = true; + } + if (mGpuToCpuSupported == false) { + // When we know the GL->CPU path works, we can call + // captureScreenImplLocked() directly, instead of using the + // glReadPixels() workaround. + useReadPixels = true; + } + } + + // this creates a "fake" BBinder which will serve as a "fake" remote + // binder to receive the marshaled calls and forward them to the + // real remote (a BpGraphicBufferProducer) + sp wrapper = new GraphicProducerWrapper(producer); + + // the asInterface() call below creates our "fake" BpGraphicBufferProducer + // which does the marshaling work forwards to our "fake remote" above. sp msg = new MessageCaptureScreen(this, - display, producer, reqWidth, reqHeight, minLayerZ, maxLayerZ, - isCpuConsumer); - status_t res = postMessageSync(msg); + display, IGraphicBufferProducer::asInterface( wrapper ), + reqWidth, reqHeight, minLayerZ, maxLayerZ, + useReadPixels); + + status_t res = postMessageAsync(msg); if (res == NO_ERROR) { - res = static_cast( msg.get() )->getResult(); + res = wrapper->waitForResponse(); } return res; }