Support Surface and Layer Readback in the SkiaPipelines.

Test: CTS TextureViewTests and UIRendering
Change-Id: I2969c8f5a975bfd9aebcbb585c64d1fcbb2487c2
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 4722050..fdf4d52 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -24,6 +24,7 @@
     pipeline/skia/ReorderBarrierDrawables.cpp \
     pipeline/skia/SkiaDisplayList.cpp \
     pipeline/skia/SkiaOpenGLPipeline.cpp \
+    pipeline/skia/SkiaOpenGLReadback.cpp \
     pipeline/skia/SkiaPipeline.cpp \
     pipeline/skia/SkiaProfileRenderer.cpp \
     pipeline/skia/SkiaRecordingCanvas.cpp \
@@ -84,6 +85,7 @@
     LayerUpdateQueue.cpp \
     Matrix.cpp \
     OpDumper.cpp \
+    OpenGLReadback.cpp \
     Patch.cpp \
     PatchCache.cpp \
     PathCache.cpp \
@@ -96,7 +98,6 @@
     Properties.cpp \
     PropertyValuesAnimatorSet.cpp \
     PropertyValuesHolder.cpp \
-    Readback.cpp \
     RecordingCanvas.cpp \
     RenderBufferCache.cpp \
     RenderNode.cpp \
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/OpenGLReadback.cpp
similarity index 82%
rename from libs/hwui/Readback.cpp
rename to libs/hwui/OpenGLReadback.cpp
index 1645218..da6d994 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/OpenGLReadback.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "Readback.h"
+#include "OpenGLReadback.h"
 
 #include "Caches.h"
 #include "Image.h"
@@ -31,8 +31,69 @@
 namespace android {
 namespace uirenderer {
 
-static CopyResult copyTextureInto(Caches& caches, RenderState& renderState,
-        Texture& sourceTexture, Matrix4& texTransform, const Rect& srcRect,
+CopyResult OpenGLReadback::copySurfaceInto(Surface& surface, const Rect& srcRect,
+        SkBitmap* bitmap) {
+    ATRACE_CALL();
+    mRenderThread.eglManager().initialize();
+
+    // Setup the source
+    sp<GraphicBuffer> sourceBuffer;
+    sp<Fence> sourceFence;
+    Matrix4 texTransform;
+    status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence,
+            texTransform.data);
+    texTransform.invalidateType();
+    if (err != NO_ERROR) {
+        ALOGW("Failed to get last queued buffer, error = %d", err);
+        return CopyResult::UnknownError;
+    }
+    if (!sourceBuffer.get()) {
+        ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
+        return CopyResult::SourceEmpty;
+    }
+    if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) {
+        ALOGW("Surface is protected, unable to copy from it");
+        return CopyResult::SourceInvalid;
+    }
+    err = sourceFence->wait(500 /* ms */);
+    if (err != NO_ERROR) {
+        ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
+        return CopyResult::Timeout;
+    }
+
+    // TODO: Can't use Image helper since it forces GL_TEXTURE_2D usage via
+    // GL_OES_EGL_image, which doesn't work since we need samplerExternalOES
+    // to be able to properly sample from the buffer.
+
+    // Create the EGLImage object that maps the GraphicBuffer
+    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    EGLClientBuffer clientBuffer = (EGLClientBuffer) sourceBuffer->getNativeBuffer();
+    EGLint attrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE };
+
+    EGLImageKHR sourceImage = eglCreateImageKHR(display, EGL_NO_CONTEXT,
+            EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs);
+
+    if (sourceImage == EGL_NO_IMAGE_KHR) {
+        ALOGW("eglCreateImageKHR failed (%#x)", eglGetError());
+        return CopyResult::UnknownError;
+    }
+
+    CopyResult copyResult = copyImageInto(sourceImage, texTransform, sourceBuffer->getWidth(),
+            sourceBuffer->getHeight(), srcRect, bitmap);
+
+    // All we're flushing & finishing is the deletion of the texture since
+    // copyImageInto already did a major flush & finish as an implicit
+    // part of glReadPixels, so this shouldn't pose any major stalls.
+    glFinish();
+    eglDestroyImageKHR(display, sourceImage);
+    return copyResult;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+inline CopyResult copyTextureInto(Caches& caches, RenderState& renderState,
+        Texture& sourceTexture, const Matrix4& texTransform, const Rect& srcRect,
         SkBitmap* bitmap) {
     int destWidth = bitmap->width();
     int destHeight = bitmap->height();
@@ -134,88 +195,40 @@
     return CopyResult::Success;
 }
 
-CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread,
-        Surface& surface, const Rect& srcRect, SkBitmap* bitmap) {
-    ATRACE_CALL();
-    renderThread.eglManager().initialize();
+CopyResult OpenGLReadbackImpl::copyImageInto(EGLImageKHR eglImage,
+        const Matrix4& imgTransform, int imgWidth, int imgHeight, const Rect& srcRect,
+        SkBitmap* bitmap) {
 
     Caches& caches = Caches::getInstance();
-
-    // Setup the source
-    sp<GraphicBuffer> sourceBuffer;
-    sp<Fence> sourceFence;
-    Matrix4 texTransform;
-    status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence,
-            texTransform.data);
-    texTransform.invalidateType();
-    if (err != NO_ERROR) {
-        ALOGW("Failed to get last queued buffer, error = %d", err);
-        return CopyResult::UnknownError;
-    }
-    if (!sourceBuffer.get()) {
-        ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
-        return CopyResult::SourceEmpty;
-    }
-    if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) {
-        ALOGW("Surface is protected, unable to copy from it");
-        return CopyResult::SourceInvalid;
-    }
-    err = sourceFence->wait(500 /* ms */);
-    if (err != NO_ERROR) {
-        ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
-        return CopyResult::Timeout;
-    }
-
-    // TODO: Can't use Image helper since it forces GL_TEXTURE_2D usage via
-    // GL_OES_EGL_image, which doesn't work since we need samplerExternalOES
-    // to be able to properly sample from the buffer.
-
-    // Create the EGLImage object that maps the GraphicBuffer
-    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-    EGLClientBuffer clientBuffer = (EGLClientBuffer) sourceBuffer->getNativeBuffer();
-    EGLint attrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE };
-
-    EGLImageKHR sourceImage = eglCreateImageKHR(display, EGL_NO_CONTEXT,
-            EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs);
-
-    if (sourceImage == EGL_NO_IMAGE_KHR) {
-        ALOGW("eglCreateImageKHR failed (%#x)", eglGetError());
-        return CopyResult::UnknownError;
-    }
     GLuint sourceTexId;
     // Create a 2D texture to sample from the EGLImage
     glGenTextures(1, &sourceTexId);
     caches.textureState().bindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId);
-    glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, sourceImage);
+    glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage);
 
     GLenum status = GL_NO_ERROR;
     while ((status = glGetError()) != GL_NO_ERROR) {
         ALOGW("glEGLImageTargetTexture2DOES failed (%#x)", status);
-        eglDestroyImageKHR(display, sourceImage);
         return CopyResult::UnknownError;
     }
 
     Texture sourceTexture(caches);
-    sourceTexture.wrap(sourceTexId, sourceBuffer->getWidth(),
-            sourceBuffer->getHeight(), 0, 0 /* total lie */, GL_TEXTURE_EXTERNAL_OES);
+    sourceTexture.wrap(sourceTexId, imgWidth, imgHeight, 0, 0 /* total lie */,
+            GL_TEXTURE_EXTERNAL_OES);
 
-    CopyResult copyResult = copyTextureInto(caches, renderThread.renderState(),
-            sourceTexture, texTransform, srcRect, bitmap);
+    CopyResult copyResult = copyTextureInto(caches, mRenderThread.renderState(),
+            sourceTexture, imgTransform, srcRect, bitmap);
     sourceTexture.deleteTexture();
-    // All we're flushing & finishing is the deletion of the texture since
-    // copyTextureInto already did a major flush & finish as an implicit
-    // part of glReadPixels, so this shouldn't pose any major stalls.
-    glFinish();
-    eglDestroyImageKHR(display, sourceImage);
     return copyResult;
 }
 
-CopyResult Readback::copyTextureLayerInto(renderthread::RenderThread& renderThread,
+bool OpenGLReadbackImpl::copyLayerInto(renderthread::RenderThread& renderThread,
         Layer& layer, SkBitmap* bitmap) {
-    ATRACE_CALL();
-    return copyTextureInto(Caches::getInstance(), renderThread.renderState(),
-            layer.getTexture(), layer.getTexTransform(), Rect(), bitmap);
+    return CopyResult::Success == copyTextureInto(Caches::getInstance(),
+            renderThread.renderState(), layer.getTexture(), layer.getTexTransform(),
+            Rect(), bitmap);
 }
 
+
 } // namespace uirenderer
 } // namespace android
diff --git a/libs/hwui/OpenGLReadback.h b/libs/hwui/OpenGLReadback.h
new file mode 100644
index 0000000..7ec2a96
--- /dev/null
+++ b/libs/hwui/OpenGLReadback.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "Readback.h"
+
+namespace android {
+namespace uirenderer {
+
+class Matrix4;
+class Layer;
+
+class OpenGLReadback : public Readback {
+public:
+    virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect,
+            SkBitmap* bitmap) override;
+
+protected:
+    explicit OpenGLReadback(renderthread::RenderThread& thread) : Readback(thread) {}
+    virtual ~OpenGLReadback() {}
+
+    virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
+            int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) = 0;
+};
+
+class OpenGLReadbackImpl : public OpenGLReadback {
+public:
+    OpenGLReadbackImpl(renderthread::RenderThread& thread) : OpenGLReadback(thread) {}
+
+    /**
+     * Copies the layer's contents into the provided bitmap.
+     */
+    static bool copyLayerInto(renderthread::RenderThread& renderThread, Layer& layer,
+            SkBitmap* bitmap);
+
+protected:
+    virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
+            int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) override;
+};
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h
index 55c943c..7fbc4bf 100644
--- a/libs/hwui/Readback.h
+++ b/libs/hwui/Readback.h
@@ -25,8 +25,6 @@
 namespace android {
 namespace uirenderer {
 
-class Layer;
-
 // Keep in sync with PixelCopy.java codes
 enum class CopyResult {
     Success = 0,
@@ -42,15 +40,14 @@
     /**
      * Copies the surface's most recently queued buffer into the provided bitmap.
      */
-    static CopyResult copySurfaceInto(renderthread::RenderThread& renderThread,
-            Surface& surface, const Rect& srcRect, SkBitmap* bitmap);
+    virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect,
+            SkBitmap* bitmap) = 0;
 
-    /**
-     * Copies the TextureLayer's texture content (thus, the currently rendering buffer) into the
-     * provided bitmap.
-     */
-    static CopyResult copyTextureLayerInto(renderthread::RenderThread& renderThread,
-            Layer& layer, SkBitmap* bitmap);
+protected:
+    explicit Readback(renderthread::RenderThread& thread) : mRenderThread(thread) {}
+    virtual ~Readback() {}
+
+    renderthread::RenderThread& mRenderThread;
 };
 
 } // namespace uirenderer
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 13a0ed8..f2af4a8 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -23,36 +23,41 @@
 namespace skiapipeline {
 
 void LayerDrawable::onDraw(SkCanvas* canvas) {
+    DrawLayer(canvas->getGrContext(), canvas, mLayer.get());
+}
+
+bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer) {
     // transform the matrix based on the layer
     int saveCount = -1;
-    if (!mLayer->getTransform().isIdentity()) {
+    if (!layer->getTransform().isIdentity()) {
         saveCount = canvas->save();
         SkMatrix transform;
-        mLayer->getTransform().copyTo(transform);
+        layer->getTransform().copyTo(transform);
         canvas->concat(transform);
     }
     GrGLTextureInfo externalTexture;
-    externalTexture.fTarget = mLayer->getRenderTarget();
-    externalTexture.fID = mLayer->getTextureId();
-    GrContext* context = canvas->getGrContext();
+    externalTexture.fTarget = layer->getRenderTarget();
+    externalTexture.fID = layer->getTextureId();
     GrBackendTextureDesc textureDescription;
-    textureDescription.fWidth = mLayer->getWidth();
-    textureDescription.fHeight = mLayer->getHeight();
+    textureDescription.fWidth = layer->getWidth();
+    textureDescription.fHeight = layer->getHeight();
     textureDescription.fConfig = kRGBA_8888_GrPixelConfig;
     textureDescription.fOrigin = kTopLeft_GrSurfaceOrigin;
     textureDescription.fTextureHandle = reinterpret_cast<GrBackendObject>(&externalTexture);
     sk_sp<SkImage> layerImage = SkImage::MakeFromTexture(context, textureDescription);
     if (layerImage) {
         SkPaint paint;
-        paint.setAlpha(mLayer->getAlpha());
-        paint.setBlendMode(mLayer->getMode());
-        paint.setColorFilter(sk_ref_sp(mLayer->getColorFilter()));
+        paint.setAlpha(layer->getAlpha());
+        paint.setBlendMode(layer->getMode());
+        paint.setColorFilter(sk_ref_sp(layer->getColorFilter()));
         canvas->drawImage(layerImage, 0, 0, &paint);
     }
     // restore the original matrix
     if (saveCount >= 0) {
         canvas->restoreToCount(saveCount);
     }
+
+    return layerImage;
 }
 
 }; // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.h b/libs/hwui/pipeline/skia/LayerDrawable.h
index 91e2744..4319895 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.h
+++ b/libs/hwui/pipeline/skia/LayerDrawable.h
@@ -33,6 +33,7 @@
     explicit LayerDrawable(Layer* layer)
             : mLayer(layer) {}
 
+    static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer);
  protected:
      virtual SkRect onGetBounds() override {
          return SkRect::MakeWH(mLayer->getWidth(), mLayer->getHeight());
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index f046e4b..7f3474a 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -17,9 +17,9 @@
 #include "SkiaOpenGLPipeline.h"
 
 #include "DeferredLayerUpdater.h"
+#include "LayerDrawable.h"
 #include "renderthread/EglManager.h"
 #include "renderstate/RenderState.h"
-#include "Readback.h"
 #include "SkiaPipeline.h"
 #include "SkiaProfileRenderer.h"
 #include "utils/TraceUtils.h"
@@ -121,10 +121,16 @@
     return *requireSwap;
 }
 
-bool SkiaOpenGLPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
-    layer->apply();
-    return Readback::copyTextureLayerInto(mRenderThread, *(layer->backingLayer()), bitmap)
-            == CopyResult::Success;
+bool SkiaOpenGLPipeline::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) {
+    if (!mRenderThread.getGrContext()) {
+        return false;
+    }
+
+    deferredLayer->apply();
+
+    SkCanvas canvas(*bitmap);
+    Layer* layer = deferredLayer->backingLayer();
+    return LayerDrawable::DrawLayer(mRenderThread.getGrContext(), &canvas, layer);
 }
 
 DeferredLayerUpdater* SkiaOpenGLPipeline::createTextureLayer() {
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
new file mode 100644
index 0000000..a18d264
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkiaOpenGLReadback.h"
+
+#include "Matrix.h"
+#include "Properties.h"
+#include <SkCanvas.h>
+#include <SkSurface.h>
+#include <gl/GrGLInterface.h>
+#include <gl/GrGLTypes.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+using namespace android::uirenderer::renderthread;
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
+        int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) {
+
+    GLuint sourceTexId;
+    glGenTextures(1, &sourceTexId);
+    glBindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId);
+    glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage);
+
+    sk_sp<GrContext> grContext = sk_ref_sp(mRenderThread.getGrContext());
+    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+        sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
+        LOG_ALWAYS_FATAL_IF(!glInterface.get());
+        grContext.reset(GrContext::Create(GrBackend::kOpenGL_GrBackend,
+                (GrBackendContext)glInterface.get()));
+    } else {
+        grContext->resetContext();
+    }
+
+    GrGLTextureInfo externalTexture;
+    externalTexture.fTarget = GL_TEXTURE_EXTERNAL_OES;
+    externalTexture.fID = sourceTexId;
+
+    GrBackendTextureDesc textureDescription;
+    textureDescription.fWidth = imgWidth;
+    textureDescription.fHeight = imgHeight;
+    textureDescription.fConfig = kRGBA_8888_GrPixelConfig;
+    textureDescription.fOrigin = kTopLeft_GrSurfaceOrigin;
+    textureDescription.fTextureHandle = reinterpret_cast<GrBackendObject>(&externalTexture);
+
+    CopyResult copyResult = CopyResult::UnknownError;
+    sk_sp<SkImage> image(SkImage::MakeFromAdoptedTexture(grContext.get(), textureDescription));
+    if (image) {
+        SkAutoLockPixels alp(*bitmap);
+
+        // convert to Skia data structures
+        const SkRect bufferRect = SkRect::MakeIWH(imgWidth, imgHeight);
+        SkRect skiaSrcRect = srcRect.toSkRect();
+        SkMatrix textureMatrix;
+        imgTransform.copyTo(textureMatrix);
+
+        // remove the y-flip applied to the matrix so that we can scale the srcRect.
+        // This flip is not needed as we specify the origin of the texture when we
+        // wrap it as an SkImage.
+        SkMatrix yFlip = SkMatrix::MakeScale(1, -1);
+        yFlip.postTranslate(0,1);
+        textureMatrix.preConcat(yFlip);
+
+        // copy the entire src if the rect is empty
+        if (skiaSrcRect.isEmpty()) {
+            skiaSrcRect = bufferRect;
+        }
+
+        // since the y-flip has been removed we can simply scale & translate
+        // the source rectangle
+        textureMatrix.mapRect(&skiaSrcRect);
+
+        if (skiaSrcRect.intersect(bufferRect)) {
+            SkPoint srcOrigin = SkPoint::Make(skiaSrcRect.fLeft, skiaSrcRect.fTop);
+
+            // if we need to scale the result we must render to an offscreen buffer
+            if (bitmap->width() != skiaSrcRect.width()
+                    || bitmap->height() != skiaSrcRect.height()) {
+                sk_sp<SkSurface> scaledSurface = SkSurface::MakeRenderTarget(
+                        grContext.get(), SkBudgeted::kYes, bitmap->info());
+                SkPaint paint;
+                paint.setBlendMode(SkBlendMode::kSrc);
+                scaledSurface->getCanvas()->drawImageRect(image, skiaSrcRect,
+                        SkRect::MakeWH(bitmap->width(), bitmap->height()), &paint);
+                image = scaledSurface->makeImageSnapshot();
+                srcOrigin.set(0,0);
+            }
+
+            if (image->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(),
+                                  srcOrigin.fX, srcOrigin.fY)) {
+                copyResult = CopyResult::Success;
+            }
+        }
+    }
+
+    // make sure that we have deleted the texture (in the SkImage) before we
+    // destroy the EGLImage that it was created from
+    image.reset();
+    return copyResult;
+}
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h
new file mode 100644
index 0000000..d914409
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "OpenGLReadback.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+class SkiaOpenGLReadback : public OpenGLReadback {
+public:
+    SkiaOpenGLReadback(renderthread::RenderThread& thread) : OpenGLReadback(thread) {}
+protected:
+    virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
+            int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) override;
+};
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/OpenGLPipeline.cpp b/libs/hwui/renderthread/OpenGLPipeline.cpp
index afeeef8..177a729 100644
--- a/libs/hwui/renderthread/OpenGLPipeline.cpp
+++ b/libs/hwui/renderthread/OpenGLPipeline.cpp
@@ -20,7 +20,7 @@
 #include "EglManager.h"
 #include "ProfileRenderer.h"
 #include "renderstate/RenderState.h"
-#include "Readback.h"
+#include "OpenGLReadback.h"
 
 #include <android/native_window.h>
 #include <cutils/properties.h>
@@ -117,9 +117,9 @@
 }
 
 bool OpenGLPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
+    ATRACE_CALL();
     layer->apply();
-    return Readback::copyTextureLayerInto(mRenderThread, *(layer->backingLayer()), bitmap)
-            == CopyResult::Success;
+    return OpenGLReadbackImpl::copyLayerInto(mRenderThread, *(layer->backingLayer()), bitmap);
 }
 
 DeferredLayerUpdater* OpenGLPipeline::createTextureLayer() {
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 39e5931..2c48242 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -602,8 +602,8 @@
 
 CREATE_BRIDGE4(copySurfaceInto, RenderThread* thread,
         Surface* surface, Rect srcRect, SkBitmap* bitmap) {
-    return (void*) Readback::copySurfaceInto(*args->thread,
-            *args->surface, args->srcRect, args->bitmap);
+    return (void*)args->thread->readback().copySurfaceInto(*args->surface,
+            args->srcRect, args->bitmap);
 }
 
 int RenderProxy::copySurfaceInto(sp<Surface>& surface, int left, int top,
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index f3789c8..223958a 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -17,8 +17,10 @@
 #include "RenderThread.h"
 
 #include "../renderstate/RenderState.h"
+#include "../pipeline/skia/SkiaOpenGLReadback.h"
 #include "CanvasContext.h"
 #include "EglManager.h"
+#include "OpenGLReadback.h"
 #include "RenderProxy.h"
 #include "VulkanManager.h"
 
@@ -196,6 +198,30 @@
     mVkManager = new VulkanManager(*this);
 }
 
+Readback& RenderThread::readback() {
+
+    if (!mReadback) {
+        auto renderType = Properties::getRenderPipelineType();
+        switch (renderType) {
+            case RenderPipelineType::OpenGL:
+                mReadback = new OpenGLReadbackImpl(*this);
+                break;
+            case RenderPipelineType::SkiaGL:
+            case RenderPipelineType::SkiaVulkan:
+                // It works to use the OpenGL pipeline for Vulkan but this is not
+                // ideal as it causes us to create an OpenGL context in addition
+                // to the Vulkan one.
+                mReadback = new skiapipeline::SkiaOpenGLReadback(*this);
+                break;
+            default:
+                LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
+                break;
+        }
+    }
+
+    return *mReadback;
+}
+
 int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
     if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
         ALOGE("Display event receiver pipe was closed or an error occurred.  "
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 12050dd..d121bcf 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -37,6 +37,7 @@
 
 namespace uirenderer {
 
+class Readback;
 class RenderState;
 class TestUtils;
 
@@ -93,6 +94,7 @@
     RenderState& renderState() const { return *mRenderState; }
     EglManager& eglManager() const { return *mEglManager; }
     JankTracker& jankTracker() { return *mJankTracker; }
+    Readback& readback();
 
     const DisplayInfo& mainDisplayInfo() { return mDisplayInfo; }
 
@@ -151,6 +153,7 @@
     EglManager* mEglManager;
 
     JankTracker* mJankTracker = nullptr;
+    Readback* mReadback = nullptr;
 
     sk_sp<GrContext> mGrContext;
     VulkanManager* mVkManager;