Refactor HWUI readback code to be backend independent

Implement readback from Surface, TextureView and HW Bitmap
for Vulkan pipeline by wrapping the graphics buffer in an SkImage.
Refactor both Vulkan and GL readback to use common code.
TextureView readback is moved from IRenderPipeline interface to
Readback class. Refactor all 3 readback flows to use common
implementation.

Test: Passed all view, uirendering and graphics CTS tests with GL
Test: Passed many CTS test with Vulkan, that require readback
Bug: 113673613
Change-Id: Ifbfd8170a5401f87a709b4b1b9fa058e8e11768d
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 8083a6a..0a25271 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1198,6 +1198,10 @@
 
 static jobject Bitmap_createHardwareBitmap(JNIEnv* env, jobject, jobject graphicBuffer) {
     sp<GraphicBuffer> buffer(graphicBufferForJavaObject(env, graphicBuffer));
+    // Bitmap::createFrom currently can only attach to a GraphicBuffer with PIXEL_FORMAT_RGBA_8888
+    // format and SRGB color space.
+    // To support any color space, we need to pass an additional ColorSpace argument to
+    // java Bitmap.createHardwareBitmap.
     sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer);
     if (!bitmap.get()) {
         ALOGW("failed to create hardware bitmap from graphic buffer");
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 4a17742..3c59bd1 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -1024,6 +1024,9 @@
         // Continue I guess?
     }
     sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer);
+    // Bitmap::createFrom currently can only attach to a GraphicBuffer with PIXEL_FORMAT_RGBA_8888
+    // format and SRGB color space.
+    // To support any color space, we could extract it from BufferItem and pass it to Bitmap.
     return bitmap::createBitmap(env, bitmap.release(),
             android::bitmap::kBitmapCreateFlag_Premultiplied);
 }
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 2baacbf..e063e0b 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -170,7 +170,6 @@
         "pipeline/skia/SkiaDisplayList.cpp",
         "pipeline/skia/SkiaMemoryTracer.cpp",
         "pipeline/skia/SkiaOpenGLPipeline.cpp",
-        "pipeline/skia/SkiaOpenGLReadback.cpp",
         "pipeline/skia/SkiaPipeline.cpp",
         "pipeline/skia/SkiaProfileRenderer.cpp",
         "pipeline/skia/SkiaRecordingCanvas.cpp",
@@ -217,13 +216,13 @@
         "Layer.cpp",
         "LayerUpdateQueue.cpp",
         "Matrix.cpp",
-        "EglReadback.cpp",
         "PathParser.cpp",
         "ProfileData.cpp",
         "ProfileDataContainer.cpp",
         "Properties.cpp",
         "PropertyValuesAnimatorSet.cpp",
         "PropertyValuesHolder.cpp",
+        "Readback.cpp",
         "RecordingCanvas.cpp",
         "RenderNode.cpp",
         "RenderProperties.cpp",
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index 0091655..837d546 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -49,6 +49,7 @@
     }
 
     mLayer->postDecStrong();
+
     mLayer = nullptr;
 }
 
diff --git a/libs/hwui/EglReadback.cpp b/libs/hwui/EglReadback.cpp
deleted file mode 100644
index 65becf8..0000000
--- a/libs/hwui/EglReadback.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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 "EglReadback.h"
-
-#include "renderthread/EglManager.h"
-
-#include <gui/Surface.h>
-#include <ui/Fence.h>
-#include <ui/GraphicBuffer.h>
-
-namespace android {
-namespace uirenderer {
-
-CopyResult EglReadback::copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap) {
-    ATRACE_CALL();
-    // 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;
-    }
-
-    return copyGraphicBufferInto(sourceBuffer.get(), texTransform, srcRect, bitmap);
-}
-
-CopyResult EglReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer, Matrix4& texTransform,
-                                              const Rect& srcRect, SkBitmap* bitmap) {
-    mRenderThread.requireGlContext();
-    // 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)graphicBuffer->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;
-    }
-
-    uint32_t width = graphicBuffer->getWidth();
-    uint32_t height = graphicBuffer->getHeight();
-    CopyResult copyResult =
-            copyImageInto(sourceImage, texTransform, width, height, srcRect, bitmap);
-
-    eglDestroyImageKHR(display, sourceImage);
-    return copyResult;
-}
-
-CopyResult EglReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer, SkBitmap* bitmap) {
-    Rect srcRect;
-    Matrix4 transform;
-    transform.loadScale(1, -1, 1);
-    transform.translate(0, -1);
-    return copyGraphicBufferInto(graphicBuffer, transform, srcRect, bitmap);
-}
-
-}  // namespace uirenderer
-}  // namespace android
diff --git a/libs/hwui/EglReadback.h b/libs/hwui/EglReadback.h
deleted file mode 100644
index e723169..0000000
--- a/libs/hwui/EglReadback.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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"
-
-#include "Matrix.h"
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-namespace android {
-namespace uirenderer {
-
-class EglReadback : public Readback {
-public:
-    virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect,
-                                       SkBitmap* bitmap) override;
-    virtual CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer,
-                                             SkBitmap* bitmap) override;
-
-protected:
-    explicit EglReadback(renderthread::RenderThread& thread) : Readback(thread) {}
-    virtual ~EglReadback() {}
-
-    virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
-                                     int imgWidth, int imgHeight, const Rect& srcRect,
-                                     SkBitmap* bitmap) = 0;
-
-private:
-    CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer, Matrix4& texTransform,
-                                     const Rect& srcRect, SkBitmap* bitmap);
-};
-
-}  // namespace uirenderer
-}  // namespace android
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index f59a2e6..cc95051 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -26,8 +26,7 @@
 
 Layer::Layer(RenderState& renderState, sk_sp<SkColorFilter> colorFilter, int alpha,
         SkBlendMode mode)
-        : GpuMemoryTracker(GpuObjectType::Layer)
-        , mRenderState(renderState)
+        : mRenderState(renderState)
         , mColorFilter(colorFilter)
         , alpha(alpha)
         , mode(mode) {
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index c4e4c1c..6f07a43 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -16,7 +16,6 @@
 
 #pragma once
 
-#include <GpuMemoryTracker.h>
 #include <utils/RefBase.h>
 
 #include <SkBlendMode.h>
@@ -39,21 +38,21 @@
 /**
  * A layer has dimensions and is backed by a backend specific texture or framebuffer.
  */
-class Layer : public VirtualLightRefBase, GpuMemoryTracker {
+class Layer : public VirtualLightRefBase {
 public:
     Layer(RenderState& renderState, sk_sp<SkColorFilter>, int alpha, SkBlendMode mode);
 
     ~Layer();
 
-    virtual uint32_t getWidth() const { return mWidth; }
+    uint32_t getWidth() const { return mWidth; }
 
-    virtual uint32_t getHeight() const { return mHeight; }
+    uint32_t getHeight() const { return mHeight; }
 
-    virtual void setSize(uint32_t width, uint32_t height) { mWidth = width; mHeight = height; }
+    void setSize(uint32_t width, uint32_t height) { mWidth = width; mHeight = height; }
 
-    virtual void setBlend(bool blend) { mBlend = blend; }
+    void setBlend(bool blend) { mBlend = blend; }
 
-    virtual bool isBlend() const { return mBlend; }
+    bool isBlend() const { return mBlend; }
 
     inline void setForceFilter(bool forceFilter) { this->forceFilter = forceFilter; }
 
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
new file mode 100644
index 0000000..80f2b57
--- /dev/null
+++ b/libs/hwui/Readback.cpp
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2018 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 "Readback.h"
+
+#include "pipeline/skia/LayerDrawable.h"
+#include "renderthread/EglManager.h"
+#include "renderthread/VulkanManager.h"
+
+#include <SkToSRGBColorFilter.h>
+#include <gui/Surface.h>
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
+#include "DeferredLayerUpdater.h"
+#include "Properties.h"
+#include "hwui/Bitmap.h"
+#include "utils/Color.h"
+#include "utils/MathUtils.h"
+
+using namespace android::uirenderer::renderthread;
+
+namespace android {
+namespace uirenderer {
+
+CopyResult Readback::copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap) {
+    ATRACE_CALL();
+    // 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;
+    }
+    if (!sourceBuffer.get()) {
+        return CopyResult::UnknownError;
+    }
+
+    sk_sp<SkColorSpace> colorSpace =
+            DataSpaceToColorSpace(static_cast<android_dataspace>(surface.getBuffersDataSpace()));
+    sk_sp<SkColorFilter> colorSpaceFilter;
+    if (colorSpace && !colorSpace->isSRGB()) {
+        colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace);
+    }
+    sk_sp<SkImage> image = SkImage::MakeFromAHardwareBuffer(
+            reinterpret_cast<AHardwareBuffer*>(sourceBuffer.get()), kPremul_SkAlphaType);
+    return copyImageInto(image, colorSpaceFilter, texTransform, srcRect, bitmap);
+}
+
+CopyResult Readback::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
+    LOG_ALWAYS_FATAL_IF(!hwBitmap->isHardware());
+
+    Rect srcRect;
+    Matrix4 transform;
+    transform.loadScale(1, -1, 1);
+    transform.translate(0, -1);
+
+    // TODO: Try to take and reuse the image inside HW bitmap with "hwBitmap->makeImage".
+    // TODO: When this was attempted, it resulted in instability.
+    sk_sp<SkColorFilter> colorSpaceFilter;
+    sk_sp<SkColorSpace> colorSpace = hwBitmap->info().refColorSpace();
+    if (colorSpace && !colorSpace->isSRGB()) {
+        colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace);
+    }
+    sk_sp<SkImage> image = SkImage::MakeFromAHardwareBuffer(
+            reinterpret_cast<AHardwareBuffer*>(hwBitmap->graphicBuffer()), kPremul_SkAlphaType);
+
+    // HW Bitmap currently can only attach to a GraphicBuffer with PIXEL_FORMAT_RGBA_8888 format
+    // and SRGB color space. ImageDecoder can create a new HW Bitmap with non-SRGB color space: for
+    // example see android.graphics.cts.BitmapColorSpaceTest#testEncodeP3hardware test.
+    return copyImageInto(image, colorSpaceFilter, transform, srcRect, bitmap);
+}
+
+CopyResult Readback::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) {
+    if (!mRenderThread.getGrContext()) {
+        return CopyResult::UnknownError;
+    }
+
+    // acquire most recent buffer for drawing
+    deferredLayer->updateTexImage();
+    deferredLayer->apply();
+    const SkRect dstRect = SkRect::MakeIWH(bitmap->width(), bitmap->height());
+    CopyResult copyResult = CopyResult::UnknownError;
+    Layer* layer = deferredLayer->backingLayer();
+    if (layer) {
+        if (copyLayerInto(layer, nullptr, &dstRect, bitmap)) {
+            copyResult = CopyResult::Success;
+        }
+    }
+    return copyResult;
+}
+
+CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image,
+                                   sk_sp<SkColorFilter>& colorSpaceFilter, Matrix4& texTransform,
+                                   const Rect& srcRect, SkBitmap* bitmap) {
+    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
+        mRenderThread.requireGlContext();
+    } else {
+        mRenderThread.vulkanManager().initialize();
+    }
+    if (!image.get()) {
+        return CopyResult::UnknownError;
+    }
+    int imgWidth = image->width();
+    int imgHeight = image->height();
+    sk_sp<GrContext> grContext = sk_ref_sp(mRenderThread.getGrContext());
+
+    if (bitmap->colorType() == kRGBA_F16_SkColorType &&
+        !grContext->colorTypeSupportedAsSurface(bitmap->colorType())) {
+        ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
+        return CopyResult::DestinationInvalid;
+    }
+
+    CopyResult copyResult = CopyResult::UnknownError;
+
+    int displayedWidth = imgWidth, displayedHeight = imgHeight;
+    // If this is a 90 or 270 degree rotation we need to swap width/height to get the device
+    // size.
+    if (texTransform[Matrix4::kSkewX] >= 0.5f || texTransform[Matrix4::kSkewX] <= -0.5f) {
+        std::swap(displayedWidth, displayedHeight);
+    }
+    SkRect skiaDestRect = SkRect::MakeWH(bitmap->width(), bitmap->height());
+    SkRect skiaSrcRect = srcRect.toSkRect();
+    if (skiaSrcRect.isEmpty()) {
+        skiaSrcRect = SkRect::MakeIWH(displayedWidth, displayedHeight);
+    }
+    bool srcNotEmpty = skiaSrcRect.intersect(SkRect::MakeIWH(displayedWidth, displayedHeight));
+    if (!srcNotEmpty) {
+        return copyResult;
+    }
+
+    // See Readback::copyLayerInto for an overview of color space conversion.
+    // HW Bitmap are allowed to be in a non-SRGB color space (for example coming from ImageDecoder).
+    // For Surface and HW Bitmap readback flows we pass colorSpaceFilter, which does the conversion.
+    // TextureView readback is using Layer::setDataSpace, which creates a SkColorFilter internally.
+    Layer layer(mRenderThread.renderState(), colorSpaceFilter, 255, SkBlendMode::kSrc);
+    bool disableFilter = MathUtils::areEqual(skiaSrcRect.width(), skiaDestRect.width()) &&
+                         MathUtils::areEqual(skiaSrcRect.height(), skiaDestRect.height());
+    layer.setForceFilter(!disableFilter);
+    layer.setSize(displayedWidth, displayedHeight);
+    texTransform.copyTo(layer.getTexTransform());
+    layer.setImage(image);
+    if (copyLayerInto(&layer, &skiaSrcRect, &skiaDestRect, bitmap)) {
+        copyResult = CopyResult::Success;
+    }
+
+    return copyResult;
+}
+
+bool Readback::copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
+                             SkBitmap* bitmap) {
+    /*
+     * In the past only TextureView readback was setting the temporary surface color space to null.
+     * Now all 3 readback flows are drawing into a SkSurface with null color space.
+     * At readback there are 3 options to convert the source image color space to the destination
+     * color space requested in "bitmap->info().colorSpace()":
+     * 1. Set color space for temporary surface render target to null (disables color management),
+     *    colorspace tag from source SkImage is ignored by Skia,
+     *    convert SkImage to SRGB at draw time with SkColorFilter/SkToSRGBColorFilter,
+     *    do a readback from temporary SkSurface to a temporary SRGB SkBitmap "bitmap2",
+     *    read back from SRGB "bitmap2" into non-SRGB "bitmap" which will do a CPU color conversion.
+     *
+     * 2. Set color space for temporary surface render target to SRGB (not nullptr),
+     *    colorspace tag on the source SkImage is used by Skia to enable conversion,
+     *    convert SkImage to SRGB at draw time with drawImage (no filters),
+     *    do a readback from temporary SkSurface, which will do a color conversion from SRGB to
+     *    bitmap->info().colorSpace() on the CPU.
+     *
+     * 3. Set color space for temporary surface render target to bitmap->info().colorSpace(),
+     *    colorspace tag on the source SkImage is used by Skia to enable conversion,
+     *    convert SkImage to bitmap->info().colorSpace() at draw time with drawImage (no filters),
+     *    do a readback from SkSurface, which will not do any color conversion, because
+     *    surface was created with the same color space as the "bitmap".
+     *
+     * Option 1 is used for all readback flows.
+     * Options 2 and 3 are new, because skia added support for non-SRGB render targets without
+     * linear blending.
+     * TODO: evaluate if options 2 or 3 for color space conversion are better.
+     */
+
+    // drop the colorSpace from the temporary surface.
+    SkImageInfo surfaceInfo = bitmap->info().makeColorSpace(nullptr);
+
+    /* This intermediate surface is present to work around a bug in SwiftShader that
+     * prevents us from reading the contents of the layer's texture directly. The
+     * workaround involves first rendering that texture into an intermediate buffer and
+     * then reading from the intermediate buffer into the bitmap.
+     * Another reason to render in an offscreen buffer is to scale and to avoid an issue b/62262733
+     * with reading incorrect data from EGLImage backed SkImage (likely a driver bug).
+     */
+    sk_sp<SkSurface> tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
+                                                              SkBudgeted::kYes, surfaceInfo);
+
+    if (!tmpSurface.get()) {
+        surfaceInfo = surfaceInfo.makeColorType(SkColorType::kN32_SkColorType);
+        tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
+                                                 surfaceInfo);
+        if (!tmpSurface.get()) {
+            ALOGW("Unable to readback GPU contents into the provided bitmap");
+            return false;
+        }
+    }
+
+    if (skiapipeline::LayerDrawable::DrawLayer(mRenderThread.getGrContext(),
+                                               tmpSurface->getCanvas(), layer, srcRect, dstRect,
+                                               false)) {
+        // If bitmap->info().colorSpace() is non-SRGB, convert the data from SRGB to non-SRGB on
+        // CPU. We can't just pass bitmap->info() to SkSurface::readPixels, because "tmpSurface" has
+        // disabled color conversion.
+        SkColorSpace* destColorSpace = bitmap->info().colorSpace();
+        SkBitmap tempSRGBBitmap;
+        SkBitmap tmpN32Bitmap;
+        SkBitmap* bitmapInSRGB;
+        if (destColorSpace && !destColorSpace->isSRGB()) {
+            tempSRGBBitmap.allocPixels(bitmap->info().makeColorSpace(SkColorSpace::MakeSRGB()));
+            bitmapInSRGB = &tempSRGBBitmap;  // Need to convert latter from SRGB to non-SRGB.
+        } else {
+            bitmapInSRGB = bitmap;  // No need for color conversion - write directly into output.
+        }
+        bool success = false;
+
+        // TODO: does any of the readbacks below clamp F16 exSRGB?
+        // Readback into a SRGB SkBitmap.
+        if (tmpSurface->readPixels(bitmapInSRGB->info(), bitmapInSRGB->getPixels(),
+                                   bitmapInSRGB->rowBytes(), 0, 0)) {
+            success = true;
+        } else {
+            // if we fail to readback from the GPU directly (e.g. 565) then we attempt to read into
+            // 8888 and then convert that into the destination format before giving up.
+            SkImageInfo bitmapInfo =
+                    SkImageInfo::MakeN32(bitmap->width(), bitmap->height(), bitmap->alphaType(),
+                                         SkColorSpace::MakeSRGB());
+            if (tmpN32Bitmap.tryAllocPixels(bitmapInfo) &&
+                tmpSurface->readPixels(bitmapInfo, tmpN32Bitmap.getPixels(),
+                                       tmpN32Bitmap.rowBytes(), 0, 0)) {
+                success = true;
+                bitmapInSRGB = &tmpN32Bitmap;
+            }
+        }
+
+        if (success) {
+            if (bitmapInSRGB != bitmap) {
+                // Convert from SRGB to non-SRGB color space if needed. Convert from N32 to
+                // destination bitmap color format if needed.
+                if (!bitmapInSRGB->readPixels(bitmap->info(), bitmap->getPixels(),
+                                              bitmap->rowBytes(), 0, 0)) {
+                    return false;
+                }
+            }
+            bitmap->notifyPixelsChanged();
+            return true;
+        }
+    }
+
+    return false;
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h
index ad3a8b6..d9e10ce 100644
--- a/libs/hwui/Readback.h
+++ b/libs/hwui/Readback.h
@@ -16,16 +16,21 @@
 
 #pragma once
 
+#include "Matrix.h"
 #include "Rect.h"
 #include "renderthread/RenderThread.h"
 
 #include <SkBitmap.h>
 
 namespace android {
+class Bitmap;
 class GraphicBuffer;
 class Surface;
 namespace uirenderer {
 
+class DeferredLayerUpdater;
+class Layer;
+
 // Keep in sync with PixelCopy.java codes
 enum class CopyResult {
     Success = 0,
@@ -38,15 +43,22 @@
 
 class Readback {
 public:
+    explicit Readback(renderthread::RenderThread& thread) : mRenderThread(thread) {}
     /**
      * Copies the surface's most recently queued buffer into the provided bitmap.
      */
-    virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap) = 0;
-    virtual CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer, SkBitmap* bitmap) = 0;
+    CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap);
 
-protected:
-    explicit Readback(renderthread::RenderThread& thread) : mRenderThread(thread) {}
-    virtual ~Readback() {}
+    CopyResult copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap);
+
+    CopyResult copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
+
+private:
+    CopyResult copyImageInto(const sk_sp<SkImage>& image, sk_sp<SkColorFilter>& colorSpaceFilter,
+                             Matrix4& texTransform, const Rect& srcRect, SkBitmap* bitmap);
+
+    bool copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
+                       SkBitmap* bitmap);
 
     renderthread::RenderThread& mRenderThread;
 };
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 3939696..440620a 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -290,7 +290,7 @@
     if (isHardware()) {
         outBitmap->allocPixels(SkImageInfo::Make(info().width(), info().height(),
                                                  info().colorType(), info().alphaType(), nullptr));
-        uirenderer::renderthread::RenderProxy::copyGraphicBufferInto(graphicBuffer(), outBitmap);
+        uirenderer::renderthread::RenderProxy::copyHWBitmapInto(this, outBitmap);
         if (mInfo.colorSpace()) {
             sk_sp<SkPixelRef> pixelRef = sk_ref_sp(outBitmap->pixelRef());
             outBitmap->setInfo(mInfo);
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index b6cd4b0..3ca0f81 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -28,12 +28,13 @@
 void LayerDrawable::onDraw(SkCanvas* canvas) {
     Layer* layer = mLayerUpdater->backingLayer();
     if (layer) {
-        DrawLayer(canvas->getGrContext(), canvas, layer);
+        DrawLayer(canvas->getGrContext(), canvas, layer, nullptr, nullptr, true);
     }
 }
 
 bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer,
-                              const SkRect* dstRect) {
+                              const SkRect* srcRect, const SkRect* dstRect,
+                              bool useLayerTransform) {
     if (context == nullptr) {
         SkDEBUGF(("Attempting to draw LayerDrawable into an unsupported surface"));
         return false;
@@ -60,12 +61,10 @@
         }
 
         SkMatrix matrix;
-        if (dstRect) {
-            // Destination rectangle is set only when we are trying to read back the content
-            // of the layer. In this case we don't want to apply layer transform.
-            matrix = textureMatrix;
-        } else {
+        if (useLayerTransform) {
             matrix = SkMatrix::Concat(layerTransform, textureMatrix);
+        } else {
+            matrix = textureMatrix;
         }
 
         SkPaint paint;
@@ -81,16 +80,26 @@
             canvas->save();
             canvas->concat(matrix);
         }
-        if (dstRect) {
+        if (dstRect || srcRect) {
             SkMatrix matrixInv;
             if (!matrix.invert(&matrixInv)) {
                 matrixInv = matrix;
             }
-            SkRect srcRect = SkRect::MakeIWH(layerWidth, layerHeight);
-            matrixInv.mapRect(&srcRect);
-            SkRect skiaDestRect = *dstRect;
+            SkRect skiaSrcRect;
+            if (srcRect) {
+                skiaSrcRect = *srcRect;
+            } else {
+                skiaSrcRect = SkRect::MakeIWH(layerWidth, layerHeight);
+            }
+            matrixInv.mapRect(&skiaSrcRect);
+            SkRect skiaDestRect;
+            if (dstRect) {
+                skiaDestRect = *dstRect;
+            } else {
+                skiaDestRect = SkRect::MakeIWH(layerWidth, layerHeight);
+            }
             matrixInv.mapRect(&skiaDestRect);
-            canvas->drawImageRect(layerImage.get(), srcRect, skiaDestRect, &paint,
+            canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, &paint,
                                   SkCanvas::kFast_SrcRectConstraint);
         } else {
             canvas->drawImage(layerImage.get(), 0, 0, &paint);
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.h b/libs/hwui/pipeline/skia/LayerDrawable.h
index 18d1184..5c12590 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.h
+++ b/libs/hwui/pipeline/skia/LayerDrawable.h
@@ -33,7 +33,7 @@
     explicit LayerDrawable(DeferredLayerUpdater* layerUpdater) : mLayerUpdater(layerUpdater) {}
 
     static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer,
-                          const SkRect* dstRect = nullptr);
+                          const SkRect* srcRect, const SkRect* dstRect, bool useLayerTransform);
 
 protected:
     virtual SkRect onGetBounds() override {
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 2ae3723..d58b59e 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -127,65 +127,6 @@
     return *requireSwap;
 }
 
-bool SkiaOpenGLPipeline::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) {
-    if (!mRenderThread.getGrContext()) {
-        return false;
-    }
-
-    // acquire most recent buffer for drawing
-    deferredLayer->updateTexImage();
-    deferredLayer->apply();
-
-    // drop the colorSpace as we only support readback into sRGB or extended sRGB
-    SkImageInfo surfaceInfo = bitmap->info().makeColorSpace(nullptr);
-
-    /* This intermediate surface is present to work around a bug in SwiftShader that
-     * prevents us from reading the contents of the layer's texture directly. The
-     * workaround involves first rendering that texture into an intermediate buffer and
-     * then reading from the intermediate buffer into the bitmap.
-     */
-    sk_sp<SkSurface> tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
-                                                              SkBudgeted::kYes, surfaceInfo);
-
-    if (!tmpSurface.get()) {
-        surfaceInfo = surfaceInfo.makeColorType(SkColorType::kN32_SkColorType);
-        tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
-                                                 surfaceInfo);
-        if (!tmpSurface.get()) {
-            ALOGW("Unable to readback GPU contents into the provided bitmap");
-            return false;
-        }
-    }
-
-    Layer* layer = deferredLayer->backingLayer();
-    const SkRect dstRect = SkRect::MakeIWH(bitmap->width(), bitmap->height());
-    if (LayerDrawable::DrawLayer(mRenderThread.getGrContext(), tmpSurface->getCanvas(), layer,
-                                 &dstRect)) {
-        sk_sp<SkImage> tmpImage = tmpSurface->makeImageSnapshot();
-        if (tmpImage->readPixels(surfaceInfo, bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
-            bitmap->notifyPixelsChanged();
-            return true;
-        }
-
-        // if we fail to readback from the GPU directly (e.g. 565) then we attempt to read into 8888
-        // and then draw that into the destination format before giving up.
-        SkBitmap tmpBitmap;
-        SkImageInfo bitmapInfo =
-                SkImageInfo::MakeN32(bitmap->width(), bitmap->height(), bitmap->alphaType());
-        if (tmpBitmap.tryAllocPixels(bitmapInfo) &&
-            tmpImage->readPixels(bitmapInfo, tmpBitmap.getPixels(), tmpBitmap.rowBytes(), 0, 0)) {
-            SkCanvas canvas(*bitmap);
-            SkPaint paint;
-            paint.setBlendMode(SkBlendMode::kSrc);
-            canvas.drawBitmap(tmpBitmap, 0, 0, &paint);
-            bitmap->notifyPixelsChanged();
-            return true;
-        }
-    }
-
-    return false;
-}
-
 DeferredLayerUpdater* SkiaOpenGLPipeline::createTextureLayer() {
     mRenderThread.requireGlContext();
     return new DeferredLayerUpdater(mRenderThread.renderState());
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
index 2e2e152..808685a 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -40,7 +40,6 @@
               FrameInfoVisualizer* profiler) override;
     bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
                      FrameInfo* currentFrameInfo, bool* requireSwap) override;
-    bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override;
     DeferredLayerUpdater* createTextureLayer() override;
     bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior,
                     renderthread::ColorMode colorMode) override;
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
deleted file mode 100644
index f2f5056..0000000
--- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * 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 <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <GrBackendSurface.h>
-#include <SkCanvas.h>
-#include <SkSurface.h>
-#include <gl/GrGLInterface.h>
-#include <gl/GrGLTypes.h>
-#include "DeviceInfo.h"
-#include "Matrix.h"
-#include "Properties.h"
-#include "utils/MathUtils.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 = GrContext::MakeGL(std::move(glInterface));
-    } else {
-        grContext->resetContext();
-    }
-
-    if (bitmap->colorType() == kRGBA_F16_SkColorType &&
-            !grContext->colorTypeSupportedAsSurface(bitmap->colorType())) {
-        ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
-        return CopyResult::DestinationInvalid;
-    }
-
-    GrGLTextureInfo externalTexture;
-    externalTexture.fTarget = GL_TEXTURE_EXTERNAL_OES;
-    externalTexture.fID = sourceTexId;
-    switch (bitmap->colorType()) {
-        case kRGBA_F16_SkColorType:
-            externalTexture.fFormat = GL_RGBA16F;
-            break;
-        case kN32_SkColorType:
-        default:
-            externalTexture.fFormat = GL_RGBA8;
-            break;
-    }
-
-    GrBackendTexture backendTexture(imgWidth, imgHeight, GrMipMapped::kNo, externalTexture);
-
-    CopyResult copyResult = CopyResult::UnknownError;
-    sk_sp<SkImage> image(SkImage::MakeFromAdoptedTexture(grContext.get(), backendTexture,
-                                                         kTopLeft_GrSurfaceOrigin,
-                                                         bitmap->colorType()));
-    if (image) {
-        int displayedWidth = imgWidth, displayedHeight = imgHeight;
-        // If this is a 90 or 270 degree rotation we need to swap width/height to get the device
-        // size.
-        if (imgTransform[Matrix4::kSkewX] >= 0.5f || imgTransform[Matrix4::kSkewX] <= -0.5f) {
-            std::swap(displayedWidth, displayedHeight);
-        }
-        SkRect skiaDestRect = SkRect::MakeWH(bitmap->width(), bitmap->height());
-        SkRect skiaSrcRect = srcRect.toSkRect();
-        if (skiaSrcRect.isEmpty()) {
-            skiaSrcRect = SkRect::MakeIWH(displayedWidth, displayedHeight);
-        }
-        bool srcNotEmpty = skiaSrcRect.intersect(SkRect::MakeIWH(displayedWidth, displayedHeight));
-
-        if (srcNotEmpty) {
-            SkMatrix textureMatrixInv;
-            imgTransform.copyTo(textureMatrixInv);
-            // TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed
-            // use bottom left origin and remove flipV and invert transformations.
-            SkMatrix flipV;
-            flipV.setAll(1, 0, 0, 0, -1, 1, 0, 0, 1);
-            textureMatrixInv.preConcat(flipV);
-            textureMatrixInv.preScale(1.0f / displayedWidth, 1.0f / displayedHeight);
-            textureMatrixInv.postScale(imgWidth, imgHeight);
-            SkMatrix textureMatrix;
-            if (!textureMatrixInv.invert(&textureMatrix)) {
-                textureMatrix = textureMatrixInv;
-            }
-
-            textureMatrixInv.mapRect(&skiaSrcRect);
-            textureMatrixInv.mapRect(&skiaDestRect);
-
-            // we render in an offscreen buffer to scale and to avoid an issue b/62262733
-            // with reading incorrect data from EGLImage backed SkImage (likely a driver bug)
-            sk_sp<SkSurface> scaledSurface =
-                    SkSurface::MakeRenderTarget(grContext.get(), SkBudgeted::kYes, bitmap->info());
-            SkPaint paint;
-            paint.setBlendMode(SkBlendMode::kSrc);
-            // Apply a filter, which is matching OpenGL pipeline readback behaviour. Filter usage
-            // is codified by tests using golden images like DecodeAccuracyTest.
-            bool disableFilter = MathUtils::areEqual(skiaSrcRect.width(), skiaDestRect.width())
-                    && MathUtils::areEqual(skiaSrcRect.height(), skiaDestRect.height());
-            if (!disableFilter) {
-                paint.setFilterQuality(kLow_SkFilterQuality);
-            }
-            scaledSurface->getCanvas()->concat(textureMatrix);
-            scaledSurface->getCanvas()->drawImageRect(image, skiaSrcRect, skiaDestRect, &paint,
-                                                      SkCanvas::kFast_SrcRectConstraint);
-
-            image = scaledSurface->makeImageSnapshot();
-
-            if (image->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
-                bitmap->notifyPixelsChanged();
-                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();
-    glFinish();
-
-    return copyResult;
-}
-
-} /* namespace skiapipeline */
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h
deleted file mode 100644
index 1ce4773..0000000
--- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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 "EglReadback.h"
-
-namespace android {
-namespace uirenderer {
-namespace skiapipeline {
-
-class SkiaOpenGLReadback : public EglReadback {
-public:
-    SkiaOpenGLReadback(renderthread::RenderThread& thread) : EglReadback(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/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 5f2eee4..611a34c 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -108,11 +108,6 @@
     return *requireSwap;
 }
 
-bool SkiaVulkanPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
-    // TODO: implement copyLayerInto for vulkan.
-    return false;
-}
-
 DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() {
     mVkManager.initialize();
 
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index 7806b42..900b054 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -38,7 +38,6 @@
               FrameInfoVisualizer* profiler) override;
     bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
                      FrameInfo* currentFrameInfo, bool* requireSwap) override;
-    bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override;
     DeferredLayerUpdater* createTextureLayer() override;
     bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior,
                     renderthread::ColorMode colorMode) override;
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanReadback.h b/libs/hwui/pipeline/skia/SkiaVulkanReadback.h
deleted file mode 100644
index 65b89d6..0000000
--- a/libs/hwui/pipeline/skia/SkiaVulkanReadback.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2018 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 {
-namespace skiapipeline {
-
-class SkiaVulkanReadback : public Readback {
-public:
-    SkiaVulkanReadback(renderthread::RenderThread& thread) : Readback(thread) {}
-
-    virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect,
-            SkBitmap* bitmap) override {
-        //TODO: implement Vulkan readback.
-        return CopyResult::UnknownError;
-    }
-
-    virtual CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer,
-            SkBitmap* bitmap) override {
-        //TODO: implement Vulkan readback.
-        return CopyResult::UnknownError;
-    }
-};
-
-} /* namespace skiapipeline */
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 8b07d1d..727cef3 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -562,10 +562,6 @@
     mPrefetchedLayers.insert(node);
 }
 
-bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
-    return mRenderPipeline->copyLayerInto(layer, bitmap);
-}
-
 void CanvasContext::destroyHardwareResources() {
     stopDrawing();
     if (mRenderPipeline->isContextReady()) {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 8ca54af..02ee72f 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -135,7 +135,6 @@
     void prepareAndDraw(RenderNode* node);
 
     void buildLayer(RenderNode* node);
-    bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
     void markLayerInUse(RenderNode* node);
 
     void destroyHardwareResources();
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index b94a758..b7b7853 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -60,7 +60,6 @@
                       FrameInfoVisualizer* profiler) = 0;
     virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
                              FrameInfo* currentFrameInfo, bool* requireSwap) = 0;
-    virtual bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) = 0;
     virtual DeferredLayerUpdater* createTextureLayer() = 0;
     virtual bool setSurface(Surface* window, SwapBehavior swapBehavior, ColorMode colorMode) = 0;
     virtual void onStop() = 0;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index e3807e6..7a5348a 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -160,8 +160,10 @@
 }
 
 bool RenderProxy::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap& bitmap) {
-    return mRenderThread.queue().runSync(
-            [&]() -> bool { return mContext->copyLayerInto(layer, &bitmap); });
+    auto& thread = RenderThread::getInstance();
+    return thread.queue().runSync(
+            [&]() -> bool { return thread.readback().copyLayerInto(layer, &bitmap)
+                                   == CopyResult::Success; });
 }
 
 void RenderProxy::pushLayerUpdate(DeferredLayerUpdater* layer) {
@@ -331,14 +333,14 @@
     }
 }
 
-int RenderProxy::copyGraphicBufferInto(GraphicBuffer* buffer, SkBitmap* bitmap) {
+int RenderProxy::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
     RenderThread& thread = RenderThread::getInstance();
     if (gettid() == thread.getTid()) {
         // TODO: fix everything that hits this. We should never be triggering a readback ourselves.
-        return (int)thread.readback().copyGraphicBufferInto(buffer, bitmap);
+        return (int)thread.readback().copyHWBitmapInto(hwBitmap, bitmap);
     } else {
         return thread.queue().runSync([&]() -> int {
-            return (int)thread.readback().copyGraphicBufferInto(buffer, bitmap);
+            return (int)thread.readback().copyHWBitmapInto(hwBitmap, bitmap);
         });
     }
 }
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index c2964a4..969ad00 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -125,7 +125,7 @@
                                            int bottom, SkBitmap* bitmap);
     ANDROID_API static void prepareToDraw(Bitmap& bitmap);
 
-    static int copyGraphicBufferInto(GraphicBuffer* buffer, SkBitmap* bitmap);
+    static int copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap);
 
     static void onBitmapDestroyed(uint32_t pixelRefId);
 
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 2322fbf..7258a0a 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -19,13 +19,12 @@
 #include "CanvasContext.h"
 #include "DeviceInfo.h"
 #include "EglManager.h"
+#include "Readback.h"
 #include "RenderProxy.h"
 #include "VulkanManager.h"
 #include "hwui/Bitmap.h"
 #include "pipeline/skia/SkiaOpenGLPipeline.h"
-#include "pipeline/skia/SkiaOpenGLReadback.h"
 #include "pipeline/skia/SkiaVulkanPipeline.h"
-#include "pipeline/skia/SkiaVulkanReadback.h"
 #include "renderstate/RenderState.h"
 #include "utils/FatVector.h"
 #include "utils/TimeUtils.h"
@@ -235,18 +234,7 @@
 
 Readback& RenderThread::readback() {
     if (!mReadback) {
-        auto renderType = Properties::getRenderPipelineType();
-        switch (renderType) {
-            case RenderPipelineType::SkiaGL:
-                mReadback = new skiapipeline::SkiaOpenGLReadback(*this);
-                break;
-            case RenderPipelineType::SkiaVulkan:
-                mReadback = new skiapipeline::SkiaVulkanReadback(*this);
-                break;
-            default:
-                LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType);
-                break;
-        }
+        mReadback = new Readback(*this);
     }
 
     return *mReadback;